Merge pull request #3758 from omnivore-app/fix/mailjet-fallback

Add an email fallback to Mailjet
This commit is contained in:
Jackson Harper 2024-03-31 19:36:47 +08:00 committed by GitHub
commit 72c750dae1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 207 additions and 173 deletions

View file

@ -86,6 +86,7 @@
"luxon": "^3.2.1",
"nanoid": "^3.1.25",
"node-html-markdown": "^1.3.0",
"node-mailjet": "^6.0.5",
"nodemailer": "^6.7.3",
"normalize-url": "^6.1.0",
"oauth": "^0.10.0",

View file

@ -1,6 +1,8 @@
import { env } from '../env'
import { generateVerificationToken } from '../utils/auth'
import { logger } from '../utils/logger'
import { sendEmail } from '../utils/sendEmail'
import mailjet from 'node-mailjet'
export const sendConfirmationEmail = async (user: {
id: string
@ -16,6 +18,10 @@ export const sendConfirmationEmail = async (user: {
link,
}
if (process.env.USE_MAILJET) {
return sendWithMailJet(user.email, link)
}
return sendEmail({
from: env.sender.message,
to: user.email,
@ -24,6 +30,44 @@ export const sendConfirmationEmail = async (user: {
})
}
const sendWithMailJet = async (
email: string,
link: string
): Promise<boolean> => {
if (!process.env.MAILJET_API_KEY || !process.env.MAILGET_SECRET_KEY) {
return false
}
const client = mailjet.apiConnect(
process.env.MAILJET_API_KEY,
process.env.MAILGET_SECRET_KEY
)
try {
const request = await client.post('send', { version: 'v3.1' }).request({
Messages: [
{
From: {
Email: 'no-reply@omnivore.app',
},
To: [
{
Email: email,
Name: 'Omnivore',
},
],
Subject: 'Your Omnivore verification link',
TextPart: `Your Omnivore verification link ${link}`,
},
],
})
} catch (err) {
logger.error('error sending with mailjet', { err })
return false
}
return true
}
export const sendVerificationEmail = async (user: {
id: string
name: string

View file

@ -46,185 +46,174 @@ export function EmailSignup(): JSX.Element {
)
return (
<VStack alignment="center">
<SpanBox css={{ color: 'red' }}>
We have temporarily suspended signup via email while we work to resolve
an issue with our email provider.
</SpanBox>
<SpanBox css={{ color: 'black' }}>
<a href="https://discord.gg/h2z5rppzz9">
Join our discord for more information
</a>
</SpanBox>
</VStack>
// <form action={`${fetchEndpoint}/auth/email-signup`} method="POST">
// <VStack
// alignment="center"
// css={{
// padding: '16px',
// background: 'white',
// minWidth: '340px',
// width: '70vw',
// maxWidth: '576px',
// borderRadius: '8px',
// border: '1px solid #3D3D3D',
// boxShadow: '#B1B1B1 9px 9px 9px -9px',
// }}
// >
// <StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>
// Sign Up
// </StyledText>
// <VStack
// css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}
// >
// <SpanBox css={{ width: '100%' }}>
// <FormLabel className="required">Email</FormLabel>
// <BorderedFormInput
// autoFocus={true}
// key="email"
// type="email"
// name="email"
// defaultValue={email}
// placeholder="Email"
// css={{ backgroundColor: 'white', color: 'black' }}
// onChange={(e) => {
// e.preventDefault()
// setEmail(e.target.value)
// }}
// required
// />
// </SpanBox>
<form action={`${fetchEndpoint}/auth/email-signup`} method="POST">
<VStack
alignment="center"
css={{
padding: '16px',
background: 'white',
minWidth: '340px',
width: '70vw',
maxWidth: '576px',
borderRadius: '8px',
border: '1px solid #3D3D3D',
boxShadow: '#B1B1B1 9px 9px 9px -9px',
}}
>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>
Sign Up
</StyledText>
<VStack
css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}
>
<SpanBox css={{ width: '100%' }}>
<FormLabel className="required">Email</FormLabel>
<BorderedFormInput
autoFocus={true}
key="email"
type="email"
name="email"
defaultValue={email}
placeholder="Email"
css={{ backgroundColor: 'white', color: 'black' }}
onChange={(e) => {
e.preventDefault()
setEmail(e.target.value)
}}
required
/>
</SpanBox>
// <SpanBox css={{ width: '100%' }}>
// <FormLabel className="required">Password</FormLabel>
// <BorderedFormInput
// key="password"
// type="password"
// name="password"
// defaultValue={password}
// placeholder="Password"
// css={{ bg: 'white', color: 'black' }}
// onChange={(e) => setPassword(e.target.value)}
// required
// />
// </SpanBox>
<SpanBox css={{ width: '100%' }}>
<FormLabel className="required">Password</FormLabel>
<BorderedFormInput
key="password"
type="password"
name="password"
defaultValue={password}
placeholder="Password"
css={{ bg: 'white', color: 'black' }}
onChange={(e) => setPassword(e.target.value)}
required
/>
</SpanBox>
// <SpanBox css={{ width: '100%' }}>
// <FormLabel className="required">Full Name</FormLabel>
// <BorderedFormInput
// key="fullname"
// type="text"
// name="name"
// defaultValue={fullname}
// placeholder="Full Name"
// css={{ bg: 'white', color: 'black' }}
// onChange={(e) => setFullname(e.target.value)}
// required
// />
// </SpanBox>
<SpanBox css={{ width: '100%' }}>
<FormLabel className="required">Full Name</FormLabel>
<BorderedFormInput
key="fullname"
type="text"
name="name"
defaultValue={fullname}
placeholder="Full Name"
css={{ bg: 'white', color: 'black' }}
onChange={(e) => setFullname(e.target.value)}
required
/>
</SpanBox>
// <SpanBox css={{ width: '100%' }}>
// <FormLabel className="required">Username</FormLabel>
// <BorderedFormInput
// key="username"
// type="text"
// name="username"
// defaultValue={username}
// placeholder="Username"
// css={{ bg: 'white', color: 'black' }}
// onChange={handleUsernameChange}
// required
// />
// </SpanBox>
// {username && username.length > 0 && usernameErrorMessage && (
// <StyledText
// style="caption"
// css={{
// m: 0,
// pl: '$2',
// color: '$error',
// alignSelf: 'flex-start',
// }}
// >
// {usernameErrorMessage}
// </StyledText>
// )}
// {isUsernameValid && (
// <StyledText
// style="caption"
// css={{
// m: 0,
// pl: '$2',
// alignSelf: 'flex-start',
// color: '$omnivoreGray',
// }}
// >
// Username is available.
// </StyledText>
// )}
// </VStack>
<SpanBox css={{ width: '100%' }}>
<FormLabel className="required">Username</FormLabel>
<BorderedFormInput
key="username"
type="text"
name="username"
defaultValue={username}
placeholder="Username"
css={{ bg: 'white', color: 'black' }}
onChange={handleUsernameChange}
required
/>
</SpanBox>
{username && username.length > 0 && usernameErrorMessage && (
<StyledText
style="caption"
css={{
m: 0,
pl: '$2',
color: '$error',
alignSelf: 'flex-start',
}}
>
{usernameErrorMessage}
</StyledText>
)}
{isUsernameValid && (
<StyledText
style="caption"
css={{
m: 0,
pl: '$2',
alignSelf: 'flex-start',
color: '$omnivoreGray',
}}
>
Username is available.
</StyledText>
)}
</VStack>
// {errorMessage && <StyledText style="error">{errorMessage}</StyledText>}
{errorMessage && <StyledText style="error">{errorMessage}</StyledText>}
// <StyledText
// style="caption"
// css={{
// p: '0px',
// color: '$omnivoreLightGray',
// }}
// >
// Omnivore will send you daily tips for your first week as a new user.
// If you don&apos;t like them you can unsubscribe.
// </StyledText>
<StyledText
style="caption"
css={{
p: '0px',
color: '$omnivoreLightGray',
}}
>
Omnivore will send you daily tips for your first week as a new user.
If you don&apos;t like them you can unsubscribe.
</StyledText>
// <HStack
// alignment="center"
// distribution="end"
// css={{
// gap: '10px',
// width: '100%',
// height: '80px',
// }}
// >
// <Button
// style={'ctaOutlineYellow'}
// css={{ color: '$omnivoreGray', borderColor: 'rgba(0, 0, 0, 0.06)' }}
// type="button"
// onClick={async () => {
// window.localStorage.removeItem('authVerified')
// window.localStorage.removeItem('authToken')
// try {
// await logoutMutation()
// } catch (e) {
// console.log('error logging out', e)
// }
// window.location.href = '/'
// }}
// >
// Cancel
// </Button>
// <Button type="submit" style={'ctaDarkYellow'}>
// Sign Up
// </Button>
// </HStack>
<HStack
alignment="center"
distribution="end"
css={{
gap: '10px',
width: '100%',
height: '80px',
}}
>
<Button
style={'ctaOutlineYellow'}
css={{ color: '$omnivoreGray', borderColor: 'rgba(0, 0, 0, 0.06)' }}
type="button"
onClick={async () => {
window.localStorage.removeItem('authVerified')
window.localStorage.removeItem('authToken')
try {
await logoutMutation()
} catch (e) {
console.log('error logging out', e)
}
window.location.href = '/'
}}
>
Cancel
</Button>
<Button type="submit" style={'ctaDarkYellow'}>
Sign Up
</Button>
</HStack>
// <StyledText
// style="action"
// css={{
// pt: '16px',
// color: '$omnivoreLightGray',
// textAlign: 'center',
// }}
// >
// Already have an account?{' '}
// <Link href="/auth/email-login" passHref legacyBehavior>
// <StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>
// Login instead
// </StyledTextSpan>
// </Link>
// </StyledText>
// <TermAndConditionsFooter />
// </VStack>
// </form>
<StyledText
style="action"
css={{
pt: '16px',
color: '$omnivoreLightGray',
textAlign: 'center',
}}
>
Already have an account?{' '}
<Link href="/auth/email-login" passHref legacyBehavior>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>
Login instead
</StyledTextSpan>
</Link>
</StyledText>
<TermAndConditionsFooter />
</VStack>
</form>
)
}