287 lines
9.0 KiB
JavaScript
287 lines
9.0 KiB
JavaScript
'use strict';
|
|
|
|
const nodemailer = require('nodemailer'),
|
|
Email = require('email-templates'),
|
|
hbs = require('handlebars'),
|
|
assert = require('assert'),
|
|
fs = require('fs-extra'),
|
|
cloneDeep = require('clone-deep'),
|
|
path = require('path'),
|
|
utils = require('./utils'),
|
|
env = require('./env'),
|
|
{ Errors, DEFAULT_LANG } = require('./constants');
|
|
const { AppInputError } = require('./app_error');
|
|
|
|
const TemplateType = Object.freeze({
|
|
PUG: 'pug', // default
|
|
HANDLEBARS: 'hbs',
|
|
});
|
|
|
|
const Templates = Object.freeze({
|
|
RESET_PASSWORD: 'reset-password',
|
|
PASSWORD_RESET: 'password-reset',
|
|
UPDATE_PAYMENT: 'update-payment',
|
|
UPDATE_BILL_ADDRESS: 'update-address',
|
|
CURRENT_SUBSCRIPTIONS: 'current-subscriptions',
|
|
SUB_RENEWAL_REMIND: 'sub-renewal-remind',
|
|
SUB_TRIAL_END_REMIND: 'sub-trial-end-remind',
|
|
});
|
|
|
|
const transporter = nodemailer.createTransport({
|
|
host: env.SMTP_HOST,
|
|
port: env.SMTP_PORT,
|
|
secure: env.SMTP_SECURE,
|
|
auth: {
|
|
user: env.SMTP_USR,
|
|
pass: env.SMTP_PWD
|
|
},
|
|
// debug: true,
|
|
// logger: true
|
|
});
|
|
|
|
function registerHbsPartials(partialsPath) {
|
|
// Ref handlebars with vars in templates: https://github.com/forwardemail/email-templates/issues/283
|
|
// hbs is your own instance of handlebars, with partials
|
|
|
|
// Register all available partials under the emails' partials folder
|
|
const partialDir = path.join(process.cwd() + partialsPath);
|
|
// const partialFiles =
|
|
fs.readdir(partialDir, (err, partialFiles) => {
|
|
if (!err && !utils.isEmptyArray(partialFiles)) {
|
|
for (let i = 0; i < partialFiles.length; i++) {
|
|
const pfile = partialFiles[i];
|
|
hbs.registerPartial(path.basename(pfile, '.hbs'), require(path.join(partialDir, pfile)));
|
|
}
|
|
// Override consolidate's handlebars instance
|
|
// email.config.views.options.engineSource.requires.handlebars = hbs;
|
|
}
|
|
});
|
|
}
|
|
registerHbsPartials('/emails/partials/');
|
|
hbs.registerHelper('$t', function (key, options) {
|
|
// Translation helper to translate message by key or with hash
|
|
return options.data.root.t({ phrase: key, locale: options.data.root.locale }, options.hash);
|
|
});
|
|
|
|
require('handlebars-helpers')(['comparison']);
|
|
|
|
const agmMailSig = ['Best,', 'AgMision Team'];
|
|
|
|
const debugging = false;
|
|
|
|
const defaultMailOps = {
|
|
transport: transporter,
|
|
preview: debugging,
|
|
send: true,
|
|
i18n: {
|
|
locales: ['en', 'es', 'pt']
|
|
}
|
|
};
|
|
|
|
function init(ops) {
|
|
if (ops) {
|
|
Object.assign({}, defaultMailOps, ops);
|
|
}
|
|
}
|
|
|
|
function _toLocale(locals) {
|
|
if (!utils.isEmptyObj(locals) && !utils.hasProperty(locals, 'locale')) {
|
|
const _locals = cloneDeep(locals);
|
|
_locals.locale = _locals.lang || DEFAULT_LANG;
|
|
delete _locals.lang;
|
|
return _locals;
|
|
}
|
|
return locals;
|
|
}
|
|
|
|
/**
|
|
* Send an email with given template and additional info
|
|
* @param {*} template the template name
|
|
* @param {*} locals additional metadata
|
|
* @returns
|
|
*/
|
|
async function sendMail(template, locals, to, from) {
|
|
const email = await createEmail({ from: from, to: to });
|
|
|
|
return email.send({
|
|
template: template,
|
|
// message: {
|
|
// to: to,
|
|
// },
|
|
locals: _toLocale(locals)
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Send a text email from to with the mail options
|
|
* @param {*} contentOps { subject: <the mail subject>, text: <the text body content> }
|
|
*/
|
|
async function sendTextMail(contentOps, to, cc) {
|
|
if (utils.isEmptyObj(contentOps))
|
|
return;
|
|
|
|
const _mailOps = Object.assign(contentOps,
|
|
{
|
|
from: 'AgMission <noreply@agnav.com>',
|
|
to: to || 'agm_admin@agnav.com'
|
|
});
|
|
if (cc) _mailOps.cc = cc;
|
|
|
|
return transporter.sendMail(_mailOps);
|
|
}
|
|
|
|
/**
|
|
* Create an Email instance
|
|
* @param {*} emailOps { from, to }
|
|
*/
|
|
async function createEmail(emailOps, templateType = TemplateType.PUG) {
|
|
|
|
let _ops = Object.assign(
|
|
{
|
|
message: {
|
|
from: emailOps && emailOps.from || 'AgMission <noreply@agnav.com>'
|
|
}
|
|
},
|
|
defaultMailOps);
|
|
if (templateType === 'hbs') {
|
|
_ops = {
|
|
..._ops, views: {
|
|
options: {
|
|
extension: 'hbs'
|
|
},
|
|
engineSource: { requires: { handlebars: hbs } }
|
|
},
|
|
/* For relative assets or ones located within the same server */
|
|
// juice: true,
|
|
// juiceResources: {
|
|
// webResources: {
|
|
// images: true,
|
|
// // relativeTo: path.join(process.cwd(), '/emails/assets'),
|
|
// }
|
|
// }
|
|
}
|
|
}
|
|
|
|
if (emailOps && emailOps.to) {
|
|
_ops.message.to = emailOps.to;
|
|
}
|
|
|
|
const email = new Email(_ops);
|
|
return email;
|
|
}
|
|
|
|
async function sendHandlebarsEmail(template, locals, to, from = null) {
|
|
|
|
assert(template, AppInputError.create(Errors.TEMPLATE_NOT_FOUND));
|
|
assert(to, AppInputError.create(Errors.TO_NOT_FOUND));
|
|
|
|
const email = await createEmail({ locale: 'pt', to: to }, TemplateType.HANDLEBARS);
|
|
|
|
await email.send({
|
|
template: template,
|
|
locals: {
|
|
..._toLocale(locals)
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Send update payment email notification so the customer can decide to update with a new payment method to continue using services
|
|
* @param {*} locals { locale: 'en', name, pmType: 'American Visa', pmEnding: '4321', hostname: 'localhost' }
|
|
* @param {*} to the destination email address.
|
|
* @param {*} from (Optional) the sender email address.
|
|
*/
|
|
async function sendUpdatePaymentEmail(locals, to, from) {
|
|
assert(locals && locals.name && locals.hostname, AppInputError.create());
|
|
|
|
locals.updatePmUrl = `https://${locals.hostname}/update-pm`;
|
|
await sendHandlebarsEmail(Templates.UPDATE_PAYMENT, locals, to, from);
|
|
}
|
|
|
|
/**
|
|
* Send update Billing Address email notification so the customer can decide to update with a valid address (for tax purpose).
|
|
* @param {*} locals { locale: 'en', userId, name, address: {line1, line2, city, state, postalCode, Country (country name)}, hostname: 'localhost' }
|
|
* @param {*} to the destination email address.
|
|
* @param {*} from (Optional) the sender email address.
|
|
*/
|
|
async function sendUpdateBillingAddressEmail(locals, to, from) {
|
|
assert(locals && locals.userId && locals.name && locals.hostname && !utils.isEmptyObj(locals.address), AppInputError.create());
|
|
|
|
locals.updateAddrUrl = `https://${locals.hostname}/update-bill-address/${locals.userId}`;
|
|
await sendHandlebarsEmail(Templates.UPDATE_BILL_ADDRESS, locals, to, from);
|
|
}
|
|
|
|
/**
|
|
* Send confirmation email with current subscriptions to the customer email address (normally after any subscription updates)
|
|
* @param {*} locals { locale: 'en', userId, name, package: [{ name, billCyle, startDate }], addon: [name, quantity, billCyle, startDate] }
|
|
* @param {*} to the destination email address.
|
|
* @param {*} from (Optional) the sender email address.
|
|
*/
|
|
async function sendCurSubcriptionsEmail(locals, to, from) {
|
|
assert(locals && locals.name, AppInputError.create());
|
|
|
|
await sendHandlebarsEmail(Templates.CURRENT_SUBSCRIPTIONS, locals, to, from);
|
|
}
|
|
|
|
/**
|
|
* Send upcoming renewal reminder of a subscription to the customer (auto-renewal enabled)
|
|
* @param {*} locals { subName, upPmDate, cardType, cardEnding, chargeAmount, hostname: 'localhost' }
|
|
* @param {*} to the destination email address.
|
|
* @param {*} from (Optional) the sender email address.
|
|
* */
|
|
async function sendSubRenewalRemindEmail(locals, to, from) {
|
|
assert(locals && locals.name && locals.hostname, AppInputError.create());
|
|
|
|
await sendHandlebarsEmail(Templates.SUB_RENEWAL_REMIND, locals, to, from);
|
|
}
|
|
|
|
/**
|
|
* Send trial ending reminder of a subscription to the customer (auto-renewal enabled)
|
|
* @param {*} locals { subName, upPmDate, cardType, cardEnding, chargeAmount, hostname: 'localhost' }
|
|
* @param {*} to the destination email address.
|
|
* @param {*} from (Optional) the sender email address.
|
|
* */
|
|
async function sendSubTrialEndingRemindEmail(locals, to, from) {
|
|
assert(locals && locals.name && locals.hostname, AppInputError.create());
|
|
|
|
locals.manageSubUrl = `https://${locals.hostname}/manage-subscription/${locals.userId}`;
|
|
await sendHandlebarsEmail(Templates.SUB_TRIAL_END_REMIND, locals, to, from);
|
|
}
|
|
|
|
// NOTE: For Testing only
|
|
async function sendTestMail(req, res) {
|
|
|
|
let sent = true;
|
|
try {
|
|
// const custId = '5a1d6c05ffb94e7875c68dd9';
|
|
// const updateUrl = `https://${req.hostname}/update-pm/${custId}`;
|
|
|
|
// await sendHandlebarsEmail(
|
|
// Templates.UPDATE_PAYMENT,
|
|
// { locale: 'en', name: 'Trung Hoang', pmType: 'American Visa', pmEnding: '4321', updatePmUrl: updateUrl },
|
|
// 'trungh@agnav.com',
|
|
// );
|
|
|
|
await sendHandlebarsEmail(
|
|
Templates.CURRENT_SUBSCRIPTIONS,
|
|
{
|
|
name: "Trung Hoang",
|
|
// package: [{ name: "ess_2", billCycle: "year", startDate: utils.timestampToDate(1692720405) }],
|
|
// addon: [{ name: "addon_1", billCycle: "month", quantity: 1, startDate: utils.timestampToDate(1692720405) }]
|
|
},
|
|
'trungh@agnav.com, trungduyhoang@gmail.com',
|
|
);
|
|
} catch (err) {
|
|
console.log(err);
|
|
sent = false;
|
|
}
|
|
|
|
res.send({ msg: `Message sent ${sent}` }).end();
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
agmMailSig, sendMail, sendTextMail, sendUpdatePaymentEmail, sendUpdateBillingAddressEmail, sendCurSubcriptionsEmail, sendSubTrialEndingRemindEmail, sendSubRenewalRemindEmail,
|
|
sendTestMail
|
|
}
|