From 8af2bb076c8956dea91d856714ecc19bd09e08c8 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Fri, 5 Apr 2024 12:16:28 +0800 Subject: [PATCH] rename send mail functions --- packages/api/src/jobs/send_email.ts | 37 ++++++++++------ packages/api/src/queue-processor.ts | 9 ++-- packages/api/src/resolvers/user/index.ts | 4 +- packages/api/src/routers/auth/auth_router.ts | 4 +- .../api/src/routers/auth/mobile/sign_in.ts | 4 +- packages/api/src/services/create_user.ts | 4 +- packages/api/src/services/send_emails.ts | 43 ++++++++----------- packages/api/src/utils/createTask.ts | 18 +++----- .../api/test/services/create_user.test.ts | 4 +- 9 files changed, 61 insertions(+), 66 deletions(-) diff --git a/packages/api/src/jobs/send_email.ts b/packages/api/src/jobs/send_email.ts index c104e8eb2..8831fd970 100644 --- a/packages/api/src/jobs/send_email.ts +++ b/packages/api/src/jobs/send_email.ts @@ -1,26 +1,37 @@ import { env } from '../env' import { sendWithMailJet } from '../services/send_emails' +import { Merge } from '../util' +import { logger } from '../utils/logger' import { sendEmail } from '../utils/sendEmail' -export const SEND_CONFIRMATION_EMAIL_JOB = 'send-confirmation-email' +export const SEND_EMAIL_JOB = 'send-email' -export interface SendConfirmationEmailData { - emailAddress: string - link: string - templateData: Record -} +type ContentType = { html: string } | { text: string } | { templateId: string } +export type SendEmailJobData = Merge< + { + emailAddress: string + subject?: string + html?: string + text?: string + templateId?: string + dynamicTemplateData?: Record + }, + ContentType +> -export const sendConfirmationEmail = async ( - data: SendConfirmationEmailData -) => { - if (process.env.USE_MAILJET) { - return sendWithMailJet(data.emailAddress, data.link) +export const sendEmailJob = async (data: SendEmailJobData) => { + if (process.env.USE_MAILJET && data.dynamicTemplateData) { + return sendWithMailJet(data.emailAddress, data.dynamicTemplateData.link) + } + + if (!data.html && !data.text && !data.templateId) { + logger.error('no email content provided', data) + return false } return sendEmail({ + ...data, from: env.sender.message, to: data.emailAddress, - templateId: env.sendgrid.confirmationTemplateId, - dynamicTemplateData: data.templateData, }) } diff --git a/packages/api/src/queue-processor.ts b/packages/api/src/queue-processor.ts index 31bca2588..aab69d3dc 100644 --- a/packages/api/src/queue-processor.ts +++ b/packages/api/src/queue-processor.ts @@ -36,10 +36,7 @@ import { import { refreshAllFeeds } from './jobs/rss/refreshAllFeeds' import { refreshFeed } from './jobs/rss/refreshFeed' import { savePageJob } from './jobs/save_page' -import { - sendConfirmationEmail, - SEND_CONFIRMATION_EMAIL_JOB, -} from './jobs/send_email' +import { sendEmailJob, SEND_EMAIL_JOB } from './jobs/send_email' import { syncReadPositionsJob, SYNC_READ_POSITIONS_JOB_NAME, @@ -161,8 +158,8 @@ export const createWorker = (connection: ConnectionOptions) => return processYouTubeTranscript(job.data) case EXPORT_ALL_ITEMS_JOB_NAME: return exportAllItems(job.data) - case SEND_CONFIRMATION_EMAIL_JOB: - return sendConfirmationEmail(job.data) + case SEND_EMAIL_JOB: + return sendEmailJob(job.data) default: logger.warning(`[queue-processor] unhandled job: ${job.name}`) } diff --git a/packages/api/src/resolvers/user/index.ts b/packages/api/src/resolvers/user/index.ts index 8303d41ea..5f794aff0 100644 --- a/packages/api/src/resolvers/user/index.ts +++ b/packages/api/src/resolvers/user/index.ts @@ -41,7 +41,7 @@ import { } from '../../generated/graphql' import { userRepository } from '../../repository/user' import { createUser } from '../../services/create_user' -import { sendVerificationEmail } from '../../services/send_emails' +import { sendAccountChangeEmail } from '../../services/send_emails' import { softDeleteUser } from '../../services/user' import { userDataToUser } from '../../utils/helpers' import { validateUsername } from '../../utils/usernamePolicy' @@ -358,7 +358,7 @@ export const updateEmailResolver = authorized< return { email } } - const result = await sendVerificationEmail({ + const result = await sendAccountChangeEmail({ id: user.id, name: user.name, email, diff --git a/packages/api/src/routers/auth/auth_router.ts b/packages/api/src/routers/auth/auth_router.ts index ec18ffcbb..86374a3d1 100644 --- a/packages/api/src/routers/auth/auth_router.ts +++ b/packages/api/src/routers/auth/auth_router.ts @@ -23,7 +23,7 @@ import { userRepository } from '../../repository/user' import { isErrorWithCode } from '../../resolvers' import { createUser } from '../../services/create_user' import { - sendConfirmationEmail, + sendNewAccountVerificationEmail, sendPasswordResetEmail, } from '../../services/send_emails' import { analytics } from '../../utils/analytics' @@ -440,7 +440,7 @@ export function authRouter() { } if (user.status === StatusType.Pending && user.email) { - await sendConfirmationEmail({ + await sendNewAccountVerificationEmail({ id: user.id, email: user.email, name: user.name, diff --git a/packages/api/src/routers/auth/mobile/sign_in.ts b/packages/api/src/routers/auth/mobile/sign_in.ts index 4af80f0a6..ee4c7778a 100644 --- a/packages/api/src/routers/auth/mobile/sign_in.ts +++ b/packages/api/src/routers/auth/mobile/sign_in.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions */ import { StatusType } from '../../../entity/user' import { userRepository } from '../../../repository/user' -import { sendConfirmationEmail } from '../../../services/send_emails' +import { sendNewAccountVerificationEmail } from '../../../services/send_emails' import { comparePassword } from '../../../utils/auth' import { logger } from '../../../utils/logger' import { decodeAppleToken } from '../apple_auth' @@ -56,7 +56,7 @@ export async function createMobileEmailSignInResponse( } if (user.status === StatusType.Pending && user.email) { - await sendConfirmationEmail({ + await sendNewAccountVerificationEmail({ id: user.id, email: user.email, name: user.name, diff --git a/packages/api/src/services/create_user.ts b/packages/api/src/services/create_user.ts index d6302ae88..4bacfd9e6 100644 --- a/packages/api/src/services/create_user.ts +++ b/packages/api/src/services/create_user.ts @@ -16,7 +16,7 @@ import { IntercomClient } from '../utils/intercom' import { logger } from '../utils/logger' import { validateUsername } from '../utils/usernamePolicy' import { addPopularReadsForNewUser } from './popular_reads' -import { sendConfirmationEmail } from './send_emails' +import { sendNewAccountVerificationEmail } from './send_emails' export const MAX_RECORDS_LIMIT = 1000 @@ -142,7 +142,7 @@ export const createUser = async (input: { }) if (input.pendingConfirmation) { - await sendConfirmationEmail(user) + await sendNewAccountVerificationEmail(user) } return [user, profile] diff --git a/packages/api/src/services/send_emails.ts b/packages/api/src/services/send_emails.ts index 3ccb56132..82e24390e 100644 --- a/packages/api/src/services/send_emails.ts +++ b/packages/api/src/services/send_emails.ts @@ -1,15 +1,14 @@ import mailjet from 'node-mailjet' import { env } from '../env' import { generateVerificationToken } from '../utils/auth' -import { enqueueConfirmationEmail } from '../utils/createTask' +import { enqueueSendEmail } from '../utils/createTask' import { logger } from '../utils/logger' -import { sendEmail } from '../utils/sendEmail' -export const sendConfirmationEmail = async (user: { +export const sendNewAccountVerificationEmail = async (user: { id: string name: string email: string -}) => { +}): Promise => { // generate confirmation link const token = generateVerificationToken({ id: user.id }) const link = `${env.client.url}/auth/confirm-email/${token}` @@ -19,11 +18,13 @@ export const sendConfirmationEmail = async (user: { link, } - await enqueueConfirmationEmail({ + const result = await enqueueSendEmail({ emailAddress: user.email, - link, - templateData: dynamicTemplateData, + dynamicTemplateData: dynamicTemplateData, + templateId: env.sendgrid.confirmationTemplateId, }) + + return !!result } export const sendWithMailJet = async ( @@ -64,7 +65,7 @@ export const sendWithMailJet = async ( return true } -export const sendVerificationEmail = async (user: { +export const sendAccountChangeEmail = async (user: { id: string name: string email: string @@ -78,16 +79,13 @@ export const sendVerificationEmail = async (user: { link, } - if (process.env.USE_MAILJET) { - return sendWithMailJet(user.email, link) - } - - return sendEmail({ - from: env.sender.message, - to: user.email, + const result = await enqueueSendEmail({ + emailAddress: user.email, + dynamicTemplateData: dynamicTemplateData, templateId: env.sendgrid.verificationTemplateId, - dynamicTemplateData, }) + + return !!result } export const sendPasswordResetEmail = async (user: { @@ -104,14 +102,11 @@ export const sendPasswordResetEmail = async (user: { link, } - if (process.env.USE_MAILJET) { - return sendWithMailJet(user.email, link) - } - - return sendEmail({ - from: env.sender.message, - to: user.email, + const result = await enqueueSendEmail({ + emailAddress: user.email, + dynamicTemplateData: dynamicTemplateData, templateId: env.sendgrid.resetPasswordTemplateId, - dynamicTemplateData, }) + + return !!result } diff --git a/packages/api/src/utils/createTask.ts b/packages/api/src/utils/createTask.ts index b42884386..c2ce23fa9 100644 --- a/packages/api/src/utils/createTask.ts +++ b/packages/api/src/utils/createTask.ts @@ -35,10 +35,7 @@ import { REFRESH_ALL_FEEDS_JOB_NAME, REFRESH_FEED_JOB_NAME, } from '../jobs/rss/refreshAllFeeds' -import { - SendConfirmationEmailData, - SEND_CONFIRMATION_EMAIL_JOB, -} from '../jobs/send_email' +import { SendEmailJobData, SEND_EMAIL_JOB } from '../jobs/send_email' import { SYNC_READ_POSITIONS_JOB_NAME } from '../jobs/sync_read_positions' import { TriggerRuleJobData, TRIGGER_RULE_JOB_NAME } from '../jobs/trigger_rule' import { @@ -73,7 +70,7 @@ export const getJobPriority = (jobName: string): number => { case UPDATE_LABELS_JOB: case UPDATE_HIGHLIGHT_JOB: case SYNC_READ_POSITIONS_JOB_NAME: - case SEND_CONFIRMATION_EMAIL_JOB: + case SEND_EMAIL_JOB: return 1 case TRIGGER_RULE_JOB_NAME: case CALL_WEBHOOK_JOB_NAME: @@ -844,20 +841,15 @@ export const enqueueExportItem = async (jobData: ExportItemJobData) => { }) } -export const enqueueConfirmationEmail = async ( - jobData: SendConfirmationEmailData -) => { +export const enqueueSendEmail = async (jobData: SendEmailJobData) => { const queue = await getBackendQueue() if (!queue) { return undefined } - return queue.add(SEND_CONFIRMATION_EMAIL_JOB, jobData, { + return queue.add(SEND_EMAIL_JOB, jobData, { attempts: 1, // only try once - priority: getJobPriority(SEND_CONFIRMATION_EMAIL_JOB), - jobId: `${SEND_CONFIRMATION_EMAIL_JOB}_${jobData.emailAddress}_${JOB_VERSION}`, // deduplication - removeOnComplete: true, - removeOnFail: true, + priority: getJobPriority(SEND_EMAIL_JOB), }) } diff --git a/packages/api/test/services/create_user.test.ts b/packages/api/test/services/create_user.test.ts index 23a210b8e..2c46acabf 100644 --- a/packages/api/test/services/create_user.test.ts +++ b/packages/api/test/services/create_user.test.ts @@ -5,7 +5,7 @@ import sinon from 'sinon' import sinonChai from 'sinon-chai' import { Filter } from '../../src/entity/filter' import { StatusType, User } from '../../src/entity/user' -import { SendConfirmationEmailData } from '../../src/jobs/send_email' +import { SendTemplatedEmailData } from '../../src/jobs/send_email' import { authTrx, getRepository } from '../../src/repository' import { findProfile } from '../../src/services/profile' import { deleteUser } from '../../src/services/user' @@ -97,7 +97,7 @@ describe('create user', () => { context('create a user with pending confirmation', () => { const name = 'pendingUser' let fake: ( - jobData: SendConfirmationEmailData + jobData: SendTemplatedEmailData ) => Promise | undefined> context('when email sends successfully', () => {