Skip to content

Commit f98e7d6

Browse files
authored
Merge pull request #3658 from processing/pr05/migrate_mail_system
pr05 Typescript Migration 12: Migrate server files related to emailing/nodemailer -- REBUILD
2 parents 79e8a5c + f10adb7 commit f98e7d6

File tree

12 files changed

+5603
-1304
lines changed

12 files changed

+5603
-1304
lines changed

package-lock.json

Lines changed: 5459 additions & 1261 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,11 @@
137137
"@testing-library/react": "^12.1.2",
138138
"@types/friendly-words": "^1.2.2",
139139
"@types/jest": "^29.5.14",
140+
"@types/mjml": "^4.7.4",
140141
"@types/node": "^16.18.126",
142+
"@types/nodemailer": "^7.0.1",
141143
"@types/passport": "^1.0.17",
144+
"@types/nodemailer-mailgun-transport": "^1.4.6",
142145
"@types/react": "^16.14.0",
143146
"@types/react-dom": "^16.9.25",
144147
"@types/react-router-dom": "^5.3.3",

server/controllers/user.controller.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import crypto from 'crypto';
22

33
import User from '../models/user';
4-
import mail from '../utils/mail';
4+
import { mailerService } from '../utils/mail';
55
import { renderEmailConfirmation, renderResetPassword } from '../views/mail';
66

77
export * from './user.controller/apiKey';
@@ -83,7 +83,7 @@ export async function createUser(req, res) {
8383
});
8484

8585
try {
86-
await mail.send(mailOptions);
86+
await mailerService.send(mailOptions);
8787
res.json(userResponse(user));
8888
} catch (mailErr) {
8989
console.error(mailErr);
@@ -155,7 +155,7 @@ export async function resetPasswordInitiate(req, res) {
155155
to: user.email
156156
});
157157

158-
await mail.send(mailOptions);
158+
await mailerService.send(mailOptions);
159159
res.json({
160160
success: true,
161161
message:
@@ -203,7 +203,7 @@ export async function emailVerificationInitiate(req, res) {
203203
to: user.email
204204
});
205205
try {
206-
await mail.send(mailOptions);
206+
await mailerService.send(mailOptions);
207207
} catch (mailErr) {
208208
res.status(500).send({ error: 'Error sending mail' });
209209
return;
@@ -334,7 +334,7 @@ export async function updateSettings(req, res) {
334334
to: user.email
335335
});
336336

337-
await mail.send(mailOptions);
337+
await mailerService.send(mailOptions);
338338
} else {
339339
await saveUser(res, user);
340340
}

server/migrations/emailConsolidation.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
moveObjectToUserInS3,
88
copyObjectInS3
99
} from '../controllers/aws.controller';
10-
import mail from '../utils/mail';
10+
import { mailerService } from '../utils/mail';
1111
import { renderAccountConsolidation } from '../views/mail';
1212

1313
const mongoConnectionString = process.env.MONGO_URL;
@@ -31,7 +31,8 @@ mongoose.connection.on('error', () => {
3131
* https://mongodb.github.io/node-mongodb-native
3232
*/
3333

34-
const agg = [ // eslint-disable-line
34+
const agg = [
35+
// eslint-disable-line
3536
{
3637
$project: {
3738
email: {
@@ -187,7 +188,7 @@ async function consolidateAccount(email) {
187188
});
188189

189190
return new Promise((resolve, reject) => {
190-
mail.send(mailOptions, (mailErr, result) => {
191+
mailerService.send(mailOptions, (mailErr, result) => {
191192
console.log('Sent email.');
192193
if (mailErr) {
193194
return reject(mailErr);
@@ -226,7 +227,7 @@ async function consolidateAccount(email) {
226227
// });
227228

228229
// return new Promise((resolve, reject) => {
229-
// mail.send(mailOptions, (mailErr, result) => {
230+
// mailerService.send(mailOptions, (mailErr, result) => {
230231
// console.log('Sent email.');
231232
// if (mailErr) {
232233
// return reject(mailErr);

server/types/email.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/** Rendered mail data for the mailer service, without the 'from' property, which will be automatically added */
2+
export interface RenderedMailerData {
3+
to: string;
4+
subject: string;
5+
html?: string;
6+
}
7+
8+
// -------- EMAIL OPTIONS --------
9+
/** Options to generate the account consolidation email */
10+
export interface AccountConsolidationEmailOptions {
11+
body: {
12+
domain: string;
13+
username: string;
14+
email: string;
15+
};
16+
to: string;
17+
}
18+
/** Options to generate the reset password email */
19+
export interface ResetPasswordEmailOptions {
20+
body: {
21+
domain: string;
22+
link: string;
23+
};
24+
to: string;
25+
}
26+
/** Options to generate the confirm email email */
27+
export interface ConfirmEmailEmailOptions {
28+
body: {
29+
domain: string;
30+
link: string;
31+
};
32+
to: string;
33+
}
34+
35+
// -------- EMAIL RENDERING TEMPLATES --------
36+
/** Base template for emails */
37+
export interface BaseEmailTemplate {
38+
domain: string;
39+
headingText: string;
40+
greetingText: string;
41+
messageText: string;
42+
directLinkText: string;
43+
noteText: string;
44+
meta: {
45+
keywords: string;
46+
description: string;
47+
};
48+
}
49+
/** Template for an email with a primary button, which contains text and a link */
50+
export interface EmailWithPrimaryButtonTemplate extends BaseEmailTemplate {
51+
link: string;
52+
buttonText: string;
53+
}
54+
/** Template for rendering the account consolidation email */
55+
export interface AccountConsolidationEmailTemplate extends BaseEmailTemplate {
56+
username: string;
57+
email: string;
58+
message2Text: string;
59+
resetPasswordLink: string;
60+
resetPasswordText: string;
61+
}
62+
/** Template for rendering the confirm email email */
63+
export type ConfirmEmailEmailTemplate = EmailWithPrimaryButtonTemplate;
64+
/** Template for rendering the reset password email */
65+
export type ResetPasswordEmailTemplate = EmailWithPrimaryButtonTemplate;

server/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './email';

server/utils/mail.js renamed to server/utils/mail.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1-
/**
2-
* Mail service wrapping around mailgun
3-
*/
4-
51
import nodemailer from 'nodemailer';
62
import mg from 'nodemailer-mailgun-transport';
3+
import { RenderedMailerData } from '../types/email';
4+
5+
if (!process.env.MAILGUN_KEY) {
6+
throw new Error('Mailgun key missing');
7+
}
78

89
const auth = {
910
api_key: process.env.MAILGUN_KEY,
1011
domain: process.env.MAILGUN_DOMAIN
1112
};
1213

14+
/** Mail service class wrapping around mailgun */
1315
class Mail {
16+
client: nodemailer.Transporter;
17+
18+
sendOptions: Pick<nodemailer.SendMailOptions, 'from'>;
19+
1420
constructor() {
1521
this.client = nodemailer.createTransport(mg({ auth }));
1622
this.sendOptions = {
1723
from: process.env.EMAIL_SENDER
1824
};
1925
}
2026

21-
async sendMail(mailOptions) {
27+
async sendMail(mailOptions: nodemailer.SendMailOptions) {
2228
try {
2329
const response = await this.client.sendMail(mailOptions);
2430
return response;
@@ -28,8 +34,8 @@ class Mail {
2834
}
2935
}
3036

31-
async send(data) {
32-
const mailOptions = {
37+
async send(data: RenderedMailerData) {
38+
const mailOptions: nodemailer.SendMailOptions = {
3339
from: this.sendOptions.from,
3440
to: data.to,
3541
subject: data.subject,
@@ -46,4 +52,7 @@ class Mail {
4652
}
4753
}
4854

49-
export default new Mail();
55+
/**
56+
* Mail service wrapping around mailgun
57+
*/
58+
export const mailerService = new Mail();

server/utils/renderMjml.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

server/utils/renderMjml.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* eslint-disable consistent-return */
2+
import mjml2html from 'mjml';
3+
4+
/** Parse template string containing mjml tags into html for nodemailer.SendMailOptions.html */
5+
export function renderMjml(template: string): string | undefined {
6+
try {
7+
const output = mjml2html(template);
8+
return output.html;
9+
} catch (e) {
10+
console.error(e);
11+
// fall through to undefined (null is not valid for nodemailer.SendMailOptions.html)
12+
return undefined;
13+
}
14+
}

server/views/consolidationMailLayout.js renamed to server/views/consolidationMailLayout.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
export default ({
1+
import type { AccountConsolidationEmailTemplate } from '../types/email';
2+
3+
/** Returns mjml for an Account Consolidation email */
4+
export const consolidationMailLayout = ({
25
domain,
36
headingText,
47
greetingText,
@@ -11,7 +14,7 @@ export default ({
1114
resetPasswordText,
1215
noteText,
1316
meta
14-
}) =>
17+
}: AccountConsolidationEmailTemplate) =>
1518
`
1619
<mjml>
1720
<mj-head>

0 commit comments

Comments
 (0)