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 };