Explorar o código

refactor: normalize email calls

KernelDeimos hai 1 ano
pai
achega
c44028f413

+ 2 - 0
doc/contributors/extensions.md

@@ -0,0 +1,2 @@
+### `vscode`
+- `es6-string-html`

+ 14 - 0
package-lock.json

@@ -5276,6 +5276,19 @@
         "url": "https://github.com/sponsors/sindresorhus"
         "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": {
     "node_modules/deep-eql": {
       "version": "4.1.3",
       "version": "4.1.3",
       "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
       "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
@@ -10859,6 +10872,7 @@
         "composite-error": "^1.0.2",
         "composite-error": "^1.0.2",
         "compression": "^1.7.4",
         "compression": "^1.7.4",
         "cookie-parser": "^1.4.6",
         "cookie-parser": "^1.4.6",
+        "dedent": "^1.5.3",
         "express": "^4.18.2",
         "express": "^4.18.2",
         "file-type": "^18.5.0",
         "file-type": "^18.5.0",
         "form-data": "^4.0.0",
         "form-data": "^4.0.0",

+ 1 - 0
packages/backend/package.json

@@ -28,6 +28,7 @@
     "composite-error": "^1.0.2",
     "composite-error": "^1.0.2",
     "compression": "^1.7.4",
     "compression": "^1.7.4",
     "cookie-parser": "^1.4.6",
     "cookie-parser": "^1.4.6",
+    "dedent": "^1.5.3",
     "express": "^4.18.2",
     "express": "^4.18.2",
     "file-type": "^18.5.0",
     "file-type": "^18.5.0",
     "form-data": "^4.0.0",
     "form-data": "^4.0.0",

+ 3 - 2
packages/backend/src/CoreModule.js

@@ -195,6 +195,9 @@ const install = async ({ services, app }) => {
 
 
     const { EdgeRateLimitService } = require('./services/abuse-prevention/EdgeRateLimitService');
     const { EdgeRateLimitService } = require('./services/abuse-prevention/EdgeRateLimitService');
     services.registerService('edge-rate-limit', EdgeRateLimitService);
     services.registerService('edge-rate-limit', EdgeRateLimitService);
+
+    const { Emailservice } = require('./services/EmailService');
+    services.registerService('email', Emailservice);
 }
 }
 
 
 const install_legacy = async ({ services }) => {
 const install_legacy = async ({ services }) => {
@@ -206,7 +209,6 @@ const install_legacy = async ({ services }) => {
     const { OperationTraceService } = require('./services/OperationTraceService');
     const { OperationTraceService } = require('./services/OperationTraceService');
     const { WSPushService } = require('./services/WSPushService');
     const { WSPushService } = require('./services/WSPushService');
     const { ReferralCodeService } = require('./services/ReferralCodeService');
     const { ReferralCodeService } = require('./services/ReferralCodeService');
-    const { Emailservice } = require('./services/EmailService');
     const { ClientOperationService } = require('./services/ClientOperationService');
     const { ClientOperationService } = require('./services/ClientOperationService');
     const { EngPortalService } = require('./services/EngPortalService');
     const { EngPortalService } = require('./services/EngPortalService');
     const { AppInformationService } = require('./services/AppInformationService');
     const { AppInformationService } = require('./services/AppInformationService');
@@ -220,7 +222,6 @@ const install_legacy = async ({ services }) => {
     services.registerService('operationTrace', OperationTraceService);
     services.registerService('operationTrace', OperationTraceService);
     services.registerService('__event-push-ws', WSPushService);
     services.registerService('__event-push-ws', WSPushService);
     services.registerService('referral-code', ReferralCodeService);
     services.registerService('referral-code', ReferralCodeService);
-    services.registerService('email', Emailservice);
     services.registerService('file-cache', FileCacheService);
     services.registerService('file-cache', FileCacheService);
     services.registerService('client-operation', ClientOperationService);
     services.registerService('client-operation', ClientOperationService);
     services.registerService('app-information', AppInformationService);
     services.registerService('app-information', AppInformationService);

+ 7 - 47
packages/backend/src/helpers.js

@@ -1547,56 +1547,16 @@ async function generate_system_fsentries(user){
 }
 }
 
 
 function send_email_verification_code(email_confirm_code, email){
 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: `<p>Hi there,</p>
-            <p><strong>${hyphenize_confirm_code(email_confirm_code)}</strong> is your email confirmation code.</p>
-            <p>Sincerely,</p>
-            <p>Puter</p>
-        `,
-    });
+    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){
 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: `<p>Hi there,</p>
-            <p>Please confirm your email address using this link: <strong><a href="${link}">${link}</a></strong>.</p>
-            <p>Sincerely,</p>
-            <p>Puter</p>
-        `,
-    });
+    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(){
 async function generate_random_username(){

+ 2 - 14
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});
         let user = await get_user({id: req.user.id});
 
 
         // send email to support
         // 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
             from: '"Puter" no-reply@puter.com', // sender address
             to: 'support@puter.com', // list of receivers
             to: 'support@puter.com', // list of receivers
             replyTo: user.email === null ? undefined : user.email,
             replyTo: user.email === null ? undefined : user.email,

+ 3 - 22
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);
         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
         // create link
         const rec_link = config.origin + '/action/set-new-password?user=' + user.uuid + '&token=' + token;
         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: `
-            <p>Hi there,</p>
-            <p>A password recovery request was issued for your account, please follow the link below to reset your password:</p>
-            <p><a href="${rec_link}">${rec_link}</a></p>
-            <p>Sincerely,</p>
-            <p>Puter</p>`,
+        const svc_email = req.services.get('email');
+        await svc_email.send_email({ email: user.email }, 'email_password_recovery', {
+            link: rec_link,
         });
         });
 
 
         // Send response
         // Send response

+ 90 - 47
packages/backend/src/services/EmailService.js

@@ -16,30 +16,22 @@
  * You should have received a copy of the GNU Affero General Public License
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
-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: `<p>Hi there,</p>
-                    <p>A new user has used your referral code. Enjoy an extra {{storage_increase}} of storage, on the house!</p>
-                    <p>Sincerely,</p>
-                    <p>Puter</p>
-                `,
-            },
-            '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: `
+            <p>Hi there,</p>
+            <p>A new user has used your referral code. Enjoy an extra {{storage_increase}} of storage, on the house!</p>
+            <p>Sincerely,</p>
+            <p>Puter</p>
+        `,
+    },
+    'approved-for-listing': {
+        subject: '\u{1f389} Your app has been approved for listing!',
+        html: `
 <p>Hi there,</p>
 <p>Hi there,</p>
 <p>
 <p>
 Exciting news! <a href="https://puter.com/app/{{app_name}}">{{app_title}}</a> is now approved and live on <a href="https://puter.com/app/app-center" target="_blank">Puter App Center</a>. It's now ready for users worldwide to discover and enjoy.
 Exciting news! <a href="https://puter.com/app/{{app_name}}">{{app_title}}</a> is now approved and live on <a href="https://puter.com/app/app-center" target="_blank">Puter App Center</a>. It's now ready for users worldwide to discover and enjoy.
@@ -51,11 +43,11 @@ Exciting news! <a href="https://puter.com/app/{{app_name}}">{{app_title}}</a> is
 <p>Best,<br />
 <p>Best,<br />
 The Puter Team
 The Puter Team
 </p>
 </p>
-                `,
-            },
-            'email_change_request': {
-                subject: '\u{1f4dd} Confirm your email change',
-                html: `
+        `,
+    },
+    'email_change_request': {
+        subject: '\u{1f4dd} Confirm your email change',
+        html: `
 <p>Hi there,</p>
 <p>Hi there,</p>
 <p>
 <p>
 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.
 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
 <p>
 <p>
 <a href="{{confirm_url}}">Confirm email change</a>
 <a href="{{confirm_url}}">Confirm email change</a>
 </p>
 </p>
-                `,
-            },
-            'email_change_notification': {
-                subject: '\u{1f4dd} Notification of email change',
-                html: `
+        `,
+    },
+    'email_change_notification': {
+        subject: '\u{1f4dd} Notification of email change',
+        html: `
 <p>Hi there,</p>
 <p>Hi there,</p>
 <p>
 <p>
 We're sending an email to let you know about a change to your account.
 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.
 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.
 If this was not you, please contact support@puter.com immediately.
 </p>
 </p>
-                `,
-            },
-        };
+        `,
+    },
+    'email_verification_code': {
+        subject: `{{code}} is your confirmation code`,
+        html: /*html*/`
+        <p>Hi there,</p>
+        <p><strong>{{code}}</strong> is your email confirmation code.</p>
+        <p>Sincerely,</p>
+        <p>Puter</p>
+        `
+    },
+    'email_verification_link': {
+        subject: `Please confirm your email`,
+        html: /*html*/`
+        <p>Hi there,</p>
+        <p>Please confirm your email address using this link: <strong><a href="{{link}}">{{link}}</a></strong>.</p>
+        <p>Sincerely,</p>
+        <p>Puter</p>
+        `
+    },
+    'email_password_recovery': {
+        subject: `Password Recovery`,
+        html: /*html*/`
+        <p>Hi there,</p>
+        <p>A password recovery request was issued for your account, please follow the link below to reset your password:</p>
+        <p><a href="{{link}}">{{link}}</a></p>
+        <p>Sincerely,</p>
+        <p>Puter</p>
+        `,
+    },
+}
+
+class Emailservice extends BaseService {
+    static MODULES = {
+        nodemailer: require('nodemailer'),
+        handlebars: require('handlebars'),
+        dedent: require('dedent'),
+    };
+
+    _construct () {
+        this.templates = TEMPLATES;
 
 
         this.template_fns = {};
         this.template_fns = {};
         for ( const k in this.templates ) {
         for ( const k in this.templates ) {
             const template = this.templates[k];
             const template = this.templates[k];
             this.template_fns[k] = values => {
             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 {
                 return {
                     ...template,
                     ...template,
+                    subject: subject(values),
                     html: html(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;
         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 email = user.email;
 
 
         const template_fn = this.template_fns[template];
         const template_fn = this.template_fns[template];
         const { subject, html } = template_fn(values);
         const { subject, html } = template_fn(values);
 
 
+        const transporter = this.get_transport_();
         transporter.sendMail({
         transporter.sendMail({
             from: '"Puter" no-reply@puter.com', // sender address
             from: '"Puter" no-reply@puter.com', // sender address
             to: email, // list of receivers
             to: email, // list of receivers
             subject, html,
             subject, html,
         });
         });
     }
     }
+
+    // simple passthrough to nodemailer
+    sendMail (params) {
+        const transporter = this.get_transport_();
+        transporter.sendMail(params);
+    }
 }
 }
 
 
 module.exports = {
 module.exports = {