From c44028f413ea53a2bd35f0d092d95b6f7bd62025 Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Mon, 29 Apr 2024 17:34:10 -0400 Subject: [PATCH] refactor: normalize email calls --- doc/contributors/extensions.md | 2 + package-lock.json | 14 ++ packages/backend/package.json | 1 + packages/backend/src/CoreModule.js | 5 +- packages/backend/src/helpers.js | 54 +------ packages/backend/src/routers/contactUs.js | 16 +- .../src/routers/send-pass-recovery-email.js | 25 +--- packages/backend/src/services/EmailService.js | 137 ++++++++++++------ 8 files changed, 122 insertions(+), 132 deletions(-) create mode 100644 doc/contributors/extensions.md diff --git a/doc/contributors/extensions.md b/doc/contributors/extensions.md new file mode 100644 index 00000000..12cd44d6 --- /dev/null +++ b/doc/contributors/extensions.md @@ -0,0 +1,2 @@ +### `vscode` +- `es6-string-html` diff --git a/package-lock.json b/package-lock.json index 3bbcc2b4..ba610ac6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5276,6 +5276,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -10859,6 +10872,7 @@ "composite-error": "^1.0.2", "compression": "^1.7.4", "cookie-parser": "^1.4.6", + "dedent": "^1.5.3", "express": "^4.18.2", "file-type": "^18.5.0", "form-data": "^4.0.0", diff --git a/packages/backend/package.json b/packages/backend/package.json index 242bd8ef..f2f12392 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -28,6 +28,7 @@ "composite-error": "^1.0.2", "compression": "^1.7.4", "cookie-parser": "^1.4.6", + "dedent": "^1.5.3", "express": "^4.18.2", "file-type": "^18.5.0", "form-data": "^4.0.0", diff --git a/packages/backend/src/CoreModule.js b/packages/backend/src/CoreModule.js index 62f20871..e66f2dad 100644 --- a/packages/backend/src/CoreModule.js +++ b/packages/backend/src/CoreModule.js @@ -195,6 +195,9 @@ const install = async ({ services, app }) => { const { EdgeRateLimitService } = require('./services/abuse-prevention/EdgeRateLimitService'); services.registerService('edge-rate-limit', EdgeRateLimitService); + + const { Emailservice } = require('./services/EmailService'); + services.registerService('email', Emailservice); } const install_legacy = async ({ services }) => { @@ -206,7 +209,6 @@ const install_legacy = async ({ services }) => { const { OperationTraceService } = require('./services/OperationTraceService'); const { WSPushService } = require('./services/WSPushService'); const { ReferralCodeService } = require('./services/ReferralCodeService'); - const { Emailservice } = require('./services/EmailService'); const { ClientOperationService } = require('./services/ClientOperationService'); const { EngPortalService } = require('./services/EngPortalService'); const { AppInformationService } = require('./services/AppInformationService'); @@ -220,7 +222,6 @@ const install_legacy = async ({ services }) => { services.registerService('operationTrace', OperationTraceService); services.registerService('__event-push-ws', WSPushService); services.registerService('referral-code', ReferralCodeService); - services.registerService('email', Emailservice); services.registerService('file-cache', FileCacheService); services.registerService('client-operation', ClientOperationService); services.registerService('app-information', AppInformationService); diff --git a/packages/backend/src/helpers.js b/packages/backend/src/helpers.js index 3dcf3ae3..2c951ae4 100644 --- a/packages/backend/src/helpers.js +++ b/packages/backend/src/helpers.js @@ -1547,56 +1547,16 @@ async function generate_system_fsentries(user){ } function send_email_verification_code(email_confirm_code, email){ - const nodemailer = require("nodemailer"); - - // send email notif - let transporter = nodemailer.createTransport({ - host: config.smtp_server, - port: config.smpt_port, - secure: true, // STARTTLS - auth: { - user: config.smtp_username, - pass: config.smtp_password, - }, - }); - - transporter.sendMail({ - from: '"Puter" no-reply@puter.com', // sender address - to: email, // list of receivers - subject: `${hyphenize_confirm_code(email_confirm_code)} is your confirmation code`, // Subject line - html: `

Hi there,

-

${hyphenize_confirm_code(email_confirm_code)} is your email confirmation code.

-

Sincerely,

-

Puter

- `, - }); + const svc_email = Context.get('services').get('email'); + svc_email.send_email({ email }, 'email_verification_code', { + code: hyphenize_confirm_code(email_confirm_code), + }) } function send_email_verification_token(email_confirm_token, email, user_uuid){ - const nodemailer = require("nodemailer"); - - // send email notif - let transporter = nodemailer.createTransport({ - host: config.smtp_server, - port: config.smpt_port, - secure: true, // STARTTLS - auth: { - user: config.smtp_username, - pass: config.smtp_password, - }, - }); - - let link = `${config.origin}/confirm-email-by-token?user_uuid=${user_uuid}&token=${email_confirm_token}`; - transporter.sendMail({ - from: '"Puter" no-reply@puter.com', // sender address - to: email, // list of receivers - subject: `Please confirm your email`, // Subject line - html: `

Hi there,

-

Please confirm your email address using this link: ${link}.

-

Sincerely,

-

Puter

- `, - }); + const svc_email = Context.get('services').get('email'); + const link = `${config.origin}/confirm-email-by-token?user_uuid=${user_uuid}&token=${email_confirm_token}`; + svc_email.send_email({ email }, 'email_verification_link', { link }); } async function generate_random_username(){ diff --git a/packages/backend/src/routers/contactUs.js b/packages/backend/src/routers/contactUs.js index b57de13d..14b25426 100644 --- a/packages/backend/src/routers/contactUs.js +++ b/packages/backend/src/routers/contactUs.js @@ -71,20 +71,8 @@ router.post('/contactUs', auth, express.json(), async (req, res, next)=>{ let user = await get_user({id: req.user.id}); // send email to support - const nodemailer = require("nodemailer"); - - // send email notif - let transporter = nodemailer.createTransport({ - host: config.smtp_server, - port: config.smpt_port, - secure: true, // STARTTLS - auth: { - user: config.smtp_username, - pass: config.smtp_password, - }, - }); - - transporter.sendMail({ + const svc_email = req.services.get('email'); + svc_email.sendMail({ from: '"Puter" no-reply@puter.com', // sender address to: 'support@puter.com', // list of receivers replyTo: user.email === null ? undefined : user.email, diff --git a/packages/backend/src/routers/send-pass-recovery-email.js b/packages/backend/src/routers/send-pass-recovery-email.js index f1fa8dd5..b9c9cac0 100644 --- a/packages/backend/src/routers/send-pass-recovery-email.js +++ b/packages/backend/src/routers/send-pass-recovery-email.js @@ -86,31 +86,12 @@ router.post('/send-pass-recovery-email', express.json(), body_parser_error_handl ); invalidate_cached_user(user); - // prepare email - let transporter = nodemailer.createTransport({ - host: config.smtp_server, - port: config.smpt_port, - secure: true, // STARTTLS - auth: { - user: config.smtp_username, - pass: config.smtp_password, - }, - }); - // create link const rec_link = config.origin + '/action/set-new-password?user=' + user.uuid + '&token=' + token; - // send email - transporter.sendMail({ - from: 'no-reply@puter.com', // sender address - to: user.email, // list of receivers - subject: "Password Recovery", // Subject line - html: ` -

Hi there,

-

A password recovery request was issued for your account, please follow the link below to reset your password:

-

${rec_link}

-

Sincerely,

-

Puter

`, + const svc_email = req.services.get('email'); + await svc_email.send_email({ email: user.email }, 'email_password_recovery', { + link: rec_link, }); // Send response diff --git a/packages/backend/src/services/EmailService.js b/packages/backend/src/services/EmailService.js index d125edc3..4b50c79f 100644 --- a/packages/backend/src/services/EmailService.js +++ b/packages/backend/src/services/EmailService.js @@ -16,30 +16,22 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -const { AdvancedBase } = require("@heyputer/puter-js-common"); -class Emailservice extends AdvancedBase { - static MODULES = { - nodemailer: require('nodemailer'), - handlebars: require('handlebars'), - }; +const BaseService = require('./BaseService'); - constructor ({ services, config }) { - super(); - this.config = config; - - this.templates = { - 'new-referral': { - subject: `You've made a referral!`, - html: `

Hi there,

-

A new user has used your referral code. Enjoy an extra {{storage_increase}} of storage, on the house!

-

Sincerely,

-

Puter

- `, - }, - 'approved-for-listing': { - subject: '\u{1f389} Your app has been approved for listing!', - html: ` +const TEMPLATES = { + 'new-referral': { + subject: `You've made a referral!`, + html: ` +

Hi there,

+

A new user has used your referral code. Enjoy an extra {{storage_increase}} of storage, on the house!

+

Sincerely,

+

Puter

+ `, + }, + 'approved-for-listing': { + subject: '\u{1f389} Your app has been approved for listing!', + html: `

Hi there,

Exciting news! {{app_title}} is now approved and live on Puter App Center. It's now ready for users worldwide to discover and enjoy. @@ -51,11 +43,11 @@ Exciting news! {{app_title}} is

Best,
The Puter Team

- `, - }, - 'email_change_request': { - subject: '\u{1f4dd} Confirm your email change', - html: ` + `, + }, + 'email_change_request': { + subject: '\u{1f4dd} Confirm your email change', + html: `

Hi there,

We received a request to link this email to the user "{{username}}" on Puter. If you made this request, please click the link below to confirm the change. If you did not make this request, please ignore this email. @@ -64,59 +56,110 @@ We received a request to link this email to the user "{{username}}" on Puter. If

Confirm email change

- `, - }, - 'email_change_notification': { - subject: '\u{1f4dd} Notification of email change', - html: ` + `, + }, + 'email_change_notification': { + subject: '\u{1f4dd} Notification of email change', + html: `

Hi there,

We're sending an email to let you know about a change to your account. We have sent a confirmation to "{{new_email}}" to confirm an email change request. If this was not you, please contact support@puter.com immediately.

- `, - }, - }; + `, + }, + 'email_verification_code': { + subject: `{{code}} is your confirmation code`, + html: /*html*/` +

Hi there,

+

{{code}} is your email confirmation code.

+

Sincerely,

+

Puter

+ ` + }, + 'email_verification_link': { + subject: `Please confirm your email`, + html: /*html*/` +

Hi there,

+

Please confirm your email address using this link: {{link}}.

+

Sincerely,

+

Puter

+ ` + }, + 'email_password_recovery': { + subject: `Password Recovery`, + html: /*html*/` +

Hi there,

+

A password recovery request was issued for your account, please follow the link below to reset your password:

+

{{link}}

+

Sincerely,

+

Puter

+ `, + }, +} + +class Emailservice extends BaseService { + static MODULES = { + nodemailer: require('nodemailer'), + handlebars: require('handlebars'), + dedent: require('dedent'), + }; + + _construct () { + this.templates = TEMPLATES; this.template_fns = {}; for ( const k in this.templates ) { const template = this.templates[k]; this.template_fns[k] = values => { - const html = this.modules.handlebars.compile(template.html); + const subject = this.modules.handlebars.compile(template.subject); + const html = + this.modules.handlebars.compile( + this.modules.dedent(template.html)); return { ...template, + subject: subject(values), html: html(values), }; } } } - async send_email (user, template, values) { - const config = this.config; + _init () { + console.log('the config', this.config); + } + + get_transport_ () { const nodemailer = this.modules.nodemailer; - let transporter = nodemailer.createTransport({ - host: config.smtp_server, - port: config.smpt_port, - secure: true, // STARTTLS - auth: { - user: config.smtp_username, - pass: config.smtp_password, - }, - }); + const config = { ...this.config }; + delete config.engine; + let transport = nodemailer.createTransport(config); + + return transport; + } + + async send_email (user, template, values) { const email = user.email; const template_fn = this.template_fns[template]; const { subject, html } = template_fn(values); + const transporter = this.get_transport_(); transporter.sendMail({ from: '"Puter" no-reply@puter.com', // sender address to: email, // list of receivers subject, html, }); } + + // simple passthrough to nodemailer + sendMail (params) { + const transporter = this.get_transport_(); + transporter.sendMail(params); + } } module.exports = {