const debug = require('debug')('talk:services:mailer'); const nodemailer = require('nodemailer'); const kue = require('./kue'); const i18n = require('./i18n'); const path = require('path'); const fs = require('fs-extra'); const _ = require('lodash'); const {TEMPLATE_LOCALS} = require('../middleware/staticTemplate'); const { SMTP_HOST, SMTP_USERNAME, SMTP_PORT, SMTP_PASSWORD, SMTP_FROM_ADDRESS, EMAIL_SUBJECT_PREFIX, } = require('../config'); // load all the templates as strings const templates = { data: {} }; // load the templates per request during development templates.render = async (name, format = 'txt', context) => { if (process.env.NODE_ENV === 'production') { // If we are in production mode, check the view cache. const view = _.get(templates.data, [name, format], null); if (view !== null) { return view(context); } } const filename = path.join(__dirname, 'email', [name, format, 'ejs'].join('.')); const file = await fs.readFile(filename, 'utf8'); const view = _.template(file); if (process.env.NODE_ENV === 'production') { // If we are in production mode, fill the view cache. _.set(templates.data, [name, format], view); } return view(context); }; const mailer = {}; // enabled is true when the required configuration is available. When testing // is enabled, we will be simulating that emails are being sent, because in a // production system, emails should and would be sent. mailer.enabled = Boolean( SMTP_HOST && SMTP_USERNAME && SMTP_PASSWORD && SMTP_FROM_ADDRESS ) || process.env.NODE_ENV === 'test'; if (mailer.enabled) { const options = { host: SMTP_HOST, auth: { user: SMTP_USERNAME, pass: SMTP_PASSWORD } }; if (SMTP_PORT) { try { options.port = parseInt(SMTP_PORT); } catch (e) { throw new Error('TALK_SMTP_PORT is not an integer'); } } else { options.port = 25; } mailer.transport = nodemailer.createTransport(options); } /** * Create the new Task kue. */ mailer.task = new kue.Task({ name: 'mailer' }); /** * send will create a new message and send it. */ mailer.send = async (options) => { if (!mailer.enabled) { const err = new Error('sending email is not enabled because required configuration is not available'); console.warn(err); return; } // Create the new locals object and attach the static locals and the i18n // framework. const locals = _.merge({}, options.locals, TEMPLATE_LOCALS, {t: i18n.t}); // Render the templates. const [ html, text, ] = await Promise.all(['html', 'txt'].map((fmt) => { return templates.render(options.template, fmt, locals); })); // Create the job to send the email later. return mailer.task.create({ title: 'Mail', message: { to: options.to, subject: `${EMAIL_SUBJECT_PREFIX} ${options.subject}`, text, html } }); }; /** * Start the queue processor for the mailer job. */ mailer.process = () => { debug(`Now processing ${mailer.task.name} jobs`); return mailer.task.process(({id, data}, done) => { debug(`Starting to send mail for Job[${id}]`); // Set the `from` field. data.message.from = SMTP_FROM_ADDRESS; // Actually send the email. mailer.transport.sendMail(data.message, (err) => { if (err) { debug(`Failed to send mail for Job[${id}]:`, err); return done(err); } debug(`Finished sending mail for Job[${id}]`); return done(); }); }); }; module.exports = mailer;