mailtrain/lib/mailer.js

240 lines
8.2 KiB
JavaScript
Raw Normal View History

2016-04-04 12:36:30 +00:00
'use strict';
let log = require('npmlog');
let config = require('config');
2016-04-04 12:36:30 +00:00
let nodemailer = require('nodemailer');
let openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt;
2016-04-04 12:36:30 +00:00
let settings = require('./models/settings');
2016-05-03 16:21:01 +00:00
let tools = require('./tools');
let db = require('./db');
2016-04-04 12:36:30 +00:00
let Handlebars = require('handlebars');
let fs = require('fs');
let path = require('path');
let templates = new Map();
let htmlToText = require('html-to-text');
2017-02-17 13:56:55 +00:00
let aws = require('aws-sdk');
2016-04-04 12:36:30 +00:00
module.exports.transport = false;
module.exports.update = () => {
createMailer(() => false);
};
module.exports.getMailer = callback => {
if (!module.exports.transport) {
return createMailer(callback);
}
callback(null, module.exports.transport);
};
module.exports.sendMail = (mail, template, callback) => {
if (!callback && typeof template === 'function') {
callback = template;
template = false;
}
if (!module.exports.transport) {
return createMailer(err => {
if (err) {
return callback(err);
}
return module.exports.sendMail(mail, template, callback);
});
}
2016-09-08 12:31:38 +00:00
if (!mail.headers) {
mail.headers = {};
}
mail.headers['X-Sending-Zone'] = 'transactional';
getTemplate(template.html, (err, htmlRenderer) => {
2016-04-04 12:36:30 +00:00
if (err) {
return callback(err);
}
if (htmlRenderer) {
mail.html = htmlRenderer(template.data || {});
}
2016-05-03 16:21:01 +00:00
tools.prepareHtml(mail.html, (err, prepareHtml) => {
if (err) {
2016-05-03 16:21:01 +00:00
// ignore
}
2016-05-03 16:21:01 +00:00
if (prepareHtml) {
mail.html = prepareHtml;
}
2016-05-03 16:21:01 +00:00
getTemplate(template.text, (err, textRenderer) => {
if (err) {
return callback(err);
}
if (textRenderer) {
mail.text = textRenderer(template.data || {});
} else if (mail.html) {
mail.text = htmlToText.fromString(mail.html, {
wordwrap: 130
});
}
2016-05-31 14:32:36 +00:00
let tryCount = 0;
let trySend = () => {
tryCount++;
module.exports.transport.sendMail(mail, (err, info) => {
if (err) {
log.error('Mail', err.stack);
if (err.responseCode && err.responseCode >= 400 && err.responseCode < 500 && tryCount <= 5) {
// temporary error, try again
log.verbose('Mail', 'Retrying after %s sec. ...', tryCount);
return setTimeout(trySend, tryCount * 1000);
}
return callback(err);
}
return callback(null, info);
});
};
setImmediate(trySend);
2016-05-03 16:21:01 +00:00
});
});
2016-04-04 12:36:30 +00:00
});
};
function getTemplate(template, callback) {
if (!template) {
return callback(null, false);
}
2016-04-04 12:36:30 +00:00
if (templates.has(template)) {
return callback(null, templates.get(template));
}
2016-04-04 12:36:30 +00:00
fs.readFile(path.join(__dirname, '..', 'views', template), 'utf-8', (err, source) => {
if (err) {
return callback(err);
}
let renderer = Handlebars.compile(source);
templates.set(template, renderer);
return callback(null, renderer);
});
}
function createMailer(callback) {
2017-02-17 13:56:55 +00:00
settings.list(['smtpHostname', 'smtpPort', 'smtpEncryption', 'smtpUser', 'smtpPass', 'smtpLog', 'smtpDisableAuth', 'smtpMaxConnections', 'smtpMaxMessages', 'smtpSelfSigned', 'pgpPrivateKey', 'pgpPassphrase', 'smtpThrottling', 'mailTransport', 'sesKey', 'sesSecret', 'sesRegion'], (err, configItems) => {
2016-04-04 12:36:30 +00:00
if (err) {
return callback(err);
}
2016-05-26 09:53:12 +00:00
let oldListeners = [];
if (module.exports.transport) {
oldListeners = module.exports.transport.listeners('idle');
module.exports.transport.removeAllListeners('idle');
module.exports.transport.removeAllListeners('stream');
2016-07-05 16:31:57 +00:00
module.exports.transport.checkThrottling = null;
2016-05-26 09:53:12 +00:00
}
2017-02-17 13:56:55 +00:00
let throttling = Number(configItems.smtpThrottling) || 0;
if (throttling) {
// convert to messages/second
throttling = 1 / (throttling / (3600 * 1000));
}
let transportOptions;
let logfunc = function () {
let args = [].slice.call(arguments);
let level = args.shift();
args.shift();
args.unshift('Mail');
2017-03-04 16:15:16 +00:00
log[level](...args);
2017-02-17 13:56:55 +00:00
};
if (configItems.mailTransport === 'smtp' || !configItems.mailTransport) {
transportOptions = {
pool: true,
host: configItems.smtpHostname,
port: Number(configItems.smtpPort) || false,
secure: configItems.smtpEncryption === 'TLS',
ignoreTLS: configItems.smtpEncryption === 'NONE',
auth: configItems.smtpDisableAuth ? false : {
user: configItems.smtpUser,
pass: configItems.smtpPass
},
debug: !!configItems.smtpLog,
logger: !configItems.smtpLog ? false : {
debug: logfunc.bind(null, 'verbose'),
info: logfunc.bind(null, 'info'),
error: logfunc.bind(null, 'error')
},
maxConnections: Number(configItems.smtpMaxConnections),
maxMessages: Number(configItems.smtpMaxMessages),
tls: {
rejectUnauthorized: !configItems.smtpSelfSigned
}
};
} else if (configItems.mailTransport === 'ses') {
transportOptions = {
SES: new aws.SES({
apiVersion: '2010-12-01',
accessKeyId: configItems.sesKey,
secretAccessKey: configItems.sesSecret,
region: configItems.sesRegion
}),
debug: !!configItems.smtpLog,
logger: !configItems.smtpLog ? false : {
debug: logfunc.bind(null, 'verbose'),
info: logfunc.bind(null, 'info'),
error: logfunc.bind(null, 'error')
},
maxConnections: Number(configItems.smtpMaxConnections),
sendingRate: throttling,
tls: {
rejectUnauthorized: !configItems.smtpSelfSigned
}
};
} else {
return callback(new Error('Invalid mail transport'));
}
module.exports.transport = nodemailer.createTransport(transportOptions, config.nodemailer);
2016-04-16 17:51:22 +00:00
module.exports.transport.use('stream', openpgpEncrypt({
signingKey: configItems.pgpPrivateKey,
passphrase: configItems.pgpPassphrase
}));
2016-04-04 12:36:30 +00:00
2016-05-26 09:53:12 +00:00
if (oldListeners.length) {
log.info('Mail', 'Reattaching %s idle listeners', oldListeners.length);
oldListeners.forEach(listener => module.exports.transport.on('idle', listener));
}
2016-07-05 16:31:57 +00:00
let lastCheck = Date.now();
2017-02-17 13:56:55 +00:00
if (configItems.mailTransport === 'smtp' || !configItems.mailTransport) {
module.exports.transport.checkThrottling = function (next) {
if (!throttling) {
return next();
}
let nextCheck = Date.now();
let checkDiff = (nextCheck - lastCheck);
lastCheck = nextCheck;
if (checkDiff < throttling) {
log.verbose('Mail', 'Throttling next message in %s sec.', (throttling - checkDiff) / 1000);
setTimeout(next, throttling - checkDiff);
} else {
next();
}
};
} else {
module.exports.transport.checkThrottling = next => next();
}
2016-07-05 16:31:57 +00:00
db.clearCache('sender', () => {
callback(null, module.exports.transport);
});
2016-04-04 12:36:30 +00:00
});
}
module.exports.getTemplate = getTemplate;