From 5ca42f711401030d3b7b09ccebf2014a7341fb69 Mon Sep 17 00:00:00 2001 From: Charlynn Nguyen Date: Sun, 11 Jan 2026 21:29:59 -0800 Subject: [PATCH 1/5] i pray i fixed my origin --- .../email_templates/memberConfirmationCode.js | 20 ++++++++ api/cloud_api/routes/Mailer.js | 48 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 api/cloud_api/email_templates/memberConfirmationCode.js diff --git a/api/cloud_api/email_templates/memberConfirmationCode.js b/api/cloud_api/email_templates/memberConfirmationCode.js new file mode 100644 index 000000000..98e2d4df4 --- /dev/null +++ b/api/cloud_api/email_templates/memberConfirmationCode.js @@ -0,0 +1,20 @@ +function membershipConfirmationCode(user, recipient, confirmCode) { + if (!confirmCode) { + throw new Error('Confirmation code is required'); + } + + return { + from: user, + to: recipient, + subject: 'SCE Membership Confirmation', + generateTextFromHTML: true, + html: ` +

>Hi,


+

+ Thank you for signing up for membership!
+ Here is your confirmation code:

+ ${confirmCode} + ` + }; +} +module.exports = { membershipConfirmationCode }; diff --git a/api/cloud_api/routes/Mailer.js b/api/cloud_api/routes/Mailer.js index dca18e3be..fb9d443d2 100644 --- a/api/cloud_api/routes/Mailer.js +++ b/api/cloud_api/routes/Mailer.js @@ -5,6 +5,7 @@ const { verification } = require('../email_templates/verification'); const { passwordReset } = require('../email_templates/passwordReset'); const { blastEmail } = require('../email_templates/blastEmail'); const { unsubscribeEmail } = require('../email_templates/unsubscribeEmail'); +const { membershipConfirmationCode } = require('../email_templates/membershipConfirmationCode'); const { OK, BAD_REQUEST @@ -134,4 +135,51 @@ router.post('/sendUnsubscribeEmail', async (req, res) => { return res.sendStatus(OK); }); +router.post('/sendMembershipConfirmationCode', async (req, res) => { + if (!ENABLED && process.env.NODE_ENV !== 'test') { + return res.sendStatus(OK); + } + const scopes = ['https://mail.google.com/']; + const pathToToken = __dirname + '/../../config/token.json'; + const apiHandler = new SceGoogleApiHandler(scopes, pathToToken); + const tokenJson = await apiHandler.checkIfTokenFileExists(); + + if (tokenJson) { + if (apiHandler.checkIfTokenIsExpired(tokenJson)) { + logger.warn('refreshing token'); + apiHandler.refreshToken(); + } + } else { + logger.warn('getting new token! ', { tokenJson }); + apiHandler.getNewToken(); + } + + const { recipientEmail, confirmationCode } = req.body; + + if (!recipientEmail || !confirmationCode) { + logger.warn('Missing recipientEmail or confirmationCode', { body: req.body }); + return res.status(BAD_REQUEST).json({ + error: 'recipientEmail and confirmationCode are required', + }); + } + + await membershipConfirmationCode(USER, req.body.recipientEmail, req.body.confirmationCode) + .then((template) => { + apiHandler + .sendEmail(template) + .then((_) => { + res.sendStatus(OK); + MetricsHandler.emailSent.inc({ type: 'membershipConfirmationCode' }); + }) + .catch((err) => { + logger.error('unable to send confirmation code: ', err); + res.sendStatus(BAD_REQUEST); + }); + }) + .catch((err) => { + logger.error('unable to send member confirmation email: ', err); + res.sendStatus(BAD_REQUEST); + }); +}); + module.exports = router; From cb70c5ad6bc2f4e7288b6130395fa60685694a5c Mon Sep 17 00:00:00 2001 From: Charlynn Nguyen Date: Mon, 12 Jan 2026 13:19:54 -0800 Subject: [PATCH 2/5] added instructions --- .../email_templates/memberConfirmationCode.js | 26 +++++++++---------- api/cloud_api/routes/Mailer.js | 11 ++++---- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/api/cloud_api/email_templates/memberConfirmationCode.js b/api/cloud_api/email_templates/memberConfirmationCode.js index 98e2d4df4..57167d191 100644 --- a/api/cloud_api/email_templates/memberConfirmationCode.js +++ b/api/cloud_api/email_templates/memberConfirmationCode.js @@ -1,20 +1,20 @@ function membershipConfirmationCode(user, recipient, confirmCode) { - if (!confirmCode) { - throw new Error('Confirmation code is required'); - } - - return { - from: user, - to: recipient, - subject: 'SCE Membership Confirmation', - generateTextFromHTML: true, - html: ` + return new Promise((resolve, reject) => { + return resolve({ + from: user, + to: recipient, + subject: 'SCE Membership Confirmation', + generateTextFromHTML: true, + html: `

>Hi,


Thank you for signing up for membership!
- Here is your confirmation code:

- ${confirmCode} + Please use the below confirmation code when you + visit your profile page on the + SCE website to verify your membership:
${confirmCode} +

` - }; + }); + }); } module.exports = { membershipConfirmationCode }; diff --git a/api/cloud_api/routes/Mailer.js b/api/cloud_api/routes/Mailer.js index fb9d443d2..45c9dadb0 100644 --- a/api/cloud_api/routes/Mailer.js +++ b/api/cloud_api/routes/Mailer.js @@ -8,7 +8,8 @@ const { unsubscribeEmail } = require('../email_templates/unsubscribeEmail'); const { membershipConfirmationCode } = require('../email_templates/membershipConfirmationCode'); const { OK, - BAD_REQUEST + BAD_REQUEST, + SERVER_ERROR } = require('../../util/constants').STATUS_CODES; const logger = require('../../util/logger'); const { googleApiKeys } = require('../../config/config.json'); @@ -163,7 +164,7 @@ router.post('/sendMembershipConfirmationCode', async (req, res) => { }); } - await membershipConfirmationCode(USER, req.body.recipientEmail, req.body.confirmationCode) + await membershipConfirmationCode(USER, recipientEmail, confirmationCode) .then((template) => { apiHandler .sendEmail(template) @@ -173,12 +174,12 @@ router.post('/sendMembershipConfirmationCode', async (req, res) => { }) .catch((err) => { logger.error('unable to send confirmation code: ', err); - res.sendStatus(BAD_REQUEST); + res.sendStatus(SERVER_ERROR); }); }) .catch((err) => { - logger.error('unable to send member confirmation email: ', err); - res.sendStatus(BAD_REQUEST); + logger.error('unable to generate member confirmation email template: ', err); + res.sendStatus(SERVER_ERROR); }); }); From cc179ee5e941947c9f54c7cc447104bf27e83f1d Mon Sep 17 00:00:00 2001 From: Charlynn Nguyen Date: Mon, 12 Jan 2026 13:36:38 -0800 Subject: [PATCH 3/5] fixed import error --- .../{memberConfirmationCode.js => membershipConfirmationCode.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename api/cloud_api/email_templates/{memberConfirmationCode.js => membershipConfirmationCode.js} (100%) diff --git a/api/cloud_api/email_templates/memberConfirmationCode.js b/api/cloud_api/email_templates/membershipConfirmationCode.js similarity index 100% rename from api/cloud_api/email_templates/memberConfirmationCode.js rename to api/cloud_api/email_templates/membershipConfirmationCode.js From 089ab7447021b1fa03dc272da04f598b61ac3fae Mon Sep 17 00:00:00 2001 From: Charlynn Nguyen Date: Mon, 12 Jan 2026 14:30:51 -0800 Subject: [PATCH 4/5] fixed template formatting --- api/cloud_api/email_templates/membershipConfirmationCode.js | 2 +- api/cloud_api/routes/Mailer.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/cloud_api/email_templates/membershipConfirmationCode.js b/api/cloud_api/email_templates/membershipConfirmationCode.js index 57167d191..ff02e9d0f 100644 --- a/api/cloud_api/email_templates/membershipConfirmationCode.js +++ b/api/cloud_api/email_templates/membershipConfirmationCode.js @@ -6,8 +6,8 @@ function membershipConfirmationCode(user, recipient, confirmCode) { subject: 'SCE Membership Confirmation', generateTextFromHTML: true, html: ` -

>Hi,


+ Hi,
Thank you for signing up for membership!
Please use the below confirmation code when you visit your profile page on the diff --git a/api/cloud_api/routes/Mailer.js b/api/cloud_api/routes/Mailer.js index 45c9dadb0..605359f29 100644 --- a/api/cloud_api/routes/Mailer.js +++ b/api/cloud_api/routes/Mailer.js @@ -148,6 +148,7 @@ router.post('/sendMembershipConfirmationCode', async (req, res) => { if (tokenJson) { if (apiHandler.checkIfTokenIsExpired(tokenJson)) { logger.warn('refreshing token'); + MetricsHandler.gcpRefreshTokenLastUpdated.set(Math.floor(Date.now() / 1000)); apiHandler.refreshToken(); } } else { From cdb7ee8fc1fa79e2cd250b3106847132062830da Mon Sep 17 00:00:00 2001 From: Charlynn Nguyen Date: Mon, 12 Jan 2026 14:40:27 -0800 Subject: [PATCH 5/5] made function to call email route --- api/main_endpoints/util/emailHelpers.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/api/main_endpoints/util/emailHelpers.js b/api/main_endpoints/util/emailHelpers.js index f9251f951..9742ff80a 100644 --- a/api/main_endpoints/util/emailHelpers.js +++ b/api/main_endpoints/util/emailHelpers.js @@ -40,4 +40,16 @@ async function sendPasswordReset(resetToken, email) { }); } -module.exports = { sendUnsubscribeEmail, sendVerificationEmail, sendPasswordReset }; +async function membershipConfirmationCode(confirmCode, email) { + return new Promise((resolve) => { + axios + .post(`${MAILER_API_URL}/Mailer/sendMembershipConfirmationCode`, { + recipientEmail: email, + confirmationCode: confirmCode + }) + .then(() => resolve(true)) + .catch(() => resolve(false)); + }); +} + +module.exports = { sendUnsubscribeEmail, sendVerificationEmail, sendPasswordReset, membershipConfirmationCode };