WiP on mailers
This commit is contained in:
parent
e97415c237
commit
a4ee1534cc
46 changed files with 1263 additions and 529 deletions
318
obsolete/lib/mailer-old.js
Normal file
318
obsolete/lib/mailer-old.js
Normal file
|
@ -0,0 +1,318 @@
|
|||
'use strict';
|
||||
|
||||
const { nodeifyFunction } = require('./nodeify');
|
||||
const getSettings = nodeifyFunction(require('../models/settings').get);
|
||||
|
||||
let log = require('npmlog');
|
||||
let config = require('config');
|
||||
let nodemailer = require('nodemailer');
|
||||
let openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt;
|
||||
let tools = require('./tools');
|
||||
let db = require('./db');
|
||||
let Handlebars = require('handlebars');
|
||||
let fs = require('fs');
|
||||
let path = require('path');
|
||||
let templates = new Map();
|
||||
let htmlToText = require('html-to-text');
|
||||
let aws = require('aws-sdk');
|
||||
let objectHash = require('object-hash');
|
||||
let mjml = require('mjml');
|
||||
|
||||
let _ = require('./translate')._;
|
||||
let util = require('util');
|
||||
|
||||
Handlebars.registerHelper('translate', function (context, options) { // eslint-disable-line prefer-arrow-callback
|
||||
if (typeof options === 'undefined' && context) {
|
||||
options = context;
|
||||
context = false;
|
||||
}
|
||||
|
||||
let result = _(options.fn(this)); // eslint-disable-line no-invalid-this
|
||||
|
||||
if (Array.isArray(context)) {
|
||||
result = util.format(result, ...context);
|
||||
}
|
||||
return new Handlebars.SafeString(result);
|
||||
});
|
||||
|
||||
async function getTransport(sendConfiguration) {
|
||||
if (!sendConfiguration) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports.transport = false;
|
||||
|
||||
module.exports.update = () => {
|
||||
createMailer();
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
if (!mail.headers) {
|
||||
mail.headers = {};
|
||||
}
|
||||
mail.headers['X-Sending-Zone'] = 'transactional';
|
||||
|
||||
getTemplate(template.html, (err, htmlRenderer) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (htmlRenderer) {
|
||||
mail.html = htmlRenderer(template.data || {});
|
||||
}
|
||||
|
||||
tools.prepareHtml(mail.html, (err, prepareHtml) => {
|
||||
if (err) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (prepareHtml) {
|
||||
mail.html = prepareHtml;
|
||||
}
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
let tryCount = 0;
|
||||
let trySend = () => {
|
||||
tryCount++;
|
||||
|
||||
module.exports.transport.sendMail(mail, (err, info) => {
|
||||
if (err) {
|
||||
log.error('Mail', err);
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function getTemplate(template, callback) {
|
||||
if (!template) {
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
let key = (typeof template === 'object') ? objectHash(template) : template;
|
||||
|
||||
if (templates.has(key)) {
|
||||
return callback(null, templates.get(key));
|
||||
}
|
||||
|
||||
let done = (source, isMjml = false) => {
|
||||
if (isMjml) {
|
||||
let compiled;
|
||||
try {
|
||||
compiled = mjml.mjml2html(source);
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (compiled.errors.length) {
|
||||
return callback(compiled.errors[0].message || compiled.errors[0]);
|
||||
}
|
||||
source = compiled.html;
|
||||
}
|
||||
let renderer = Handlebars.compile(source);
|
||||
templates.set(key, renderer);
|
||||
callback(null, renderer);
|
||||
};
|
||||
|
||||
if (typeof template === 'object') {
|
||||
tools.mergeTemplateIntoLayout(template.template, template.layout, (err, source) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
let isMjml = template.type === 'mjml';
|
||||
done(source, isMjml);
|
||||
});
|
||||
} else {
|
||||
fs.readFile(path.join(__dirname, '..', 'views', template), 'utf-8', (err, source) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
done(source);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createMailer(callback) {
|
||||
// FIXME
|
||||
module.exports.transport = {
|
||||
on: () => {},
|
||||
isIdle: () => true,
|
||||
checkThrottling: next => next()
|
||||
};
|
||||
return callback(null, module.exports.transport);
|
||||
|
||||
|
||||
getSettings(['smtpHostname', 'smtpPort', 'smtpEncryption', 'smtpUser', 'smtpPass', 'smtpLog', 'smtpDisableAuth', 'smtpMaxConnections', 'smtpMaxMessages', 'smtpSelfSigned', 'pgpPrivateKey', 'pgpPassphrase', 'smtpThrottling', 'mailTransport', 'sesKey', 'sesSecret', 'sesRegion'], (err, configItems) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let oldListeners = [];
|
||||
if (module.exports.transport) {
|
||||
oldListeners = module.exports.transport.listeners('idle');
|
||||
module.exports.transport.removeAllListeners('idle');
|
||||
module.exports.transport.removeAllListeners('stream');
|
||||
module.exports.transport.checkThrottling = null;
|
||||
}
|
||||
|
||||
let sendingRate = Number(configItems.smtpThrottling) || 0;
|
||||
if (sendingRate) {
|
||||
// convert to messages/second
|
||||
sendingRate = sendingRate / 3600;
|
||||
}
|
||||
|
||||
let transportOptions;
|
||||
|
||||
let logfunc = function () {
|
||||
let args = [].slice.call(arguments);
|
||||
let level = args.shift();
|
||||
args.shift();
|
||||
args.unshift('Mail');
|
||||
log[level](...args);
|
||||
};
|
||||
|
||||
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,
|
||||
tls: {
|
||||
rejectUnauthorized: !configItems.smtpSelfSigned
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return callback(new Error(_('Invalid mail transport')));
|
||||
}
|
||||
|
||||
module.exports.transport = nodemailer.createTransport(transportOptions, config.nodemailer);
|
||||
|
||||
module.exports.transport.use('stream', openpgpEncrypt({
|
||||
signingKey: configItems.pgpPrivateKey,
|
||||
passphrase: configItems.pgpPassphrase
|
||||
}));
|
||||
|
||||
if (oldListeners.length) {
|
||||
log.info('Mail', 'Reattaching %s idle listeners', oldListeners.length);
|
||||
oldListeners.forEach(listener => module.exports.transport.on('idle', listener));
|
||||
}
|
||||
|
||||
|
||||
if (configItems.mailTransport === 'smtp' || !configItems.mailTransport) {
|
||||
|
||||
let throttling = Number(configItems.smtpThrottling) || 0;
|
||||
if (throttling) {
|
||||
throttling = 1 / (throttling / (3600 * 1000));
|
||||
}
|
||||
|
||||
let lastCheck = Date.now();
|
||||
|
||||
module.exports.transport.checkThrottling = function (next) {
|
||||
if (!throttling) {
|
||||
return next();
|
||||
}
|
||||
let nextCheck = Date.now();
|
||||
let checkDiff = (nextCheck - lastCheck);
|
||||
if (checkDiff < throttling) {
|
||||
log.verbose('Mail', 'Throttling next message in %s sec.', (throttling - checkDiff) / 1000);
|
||||
setTimeout(() => {
|
||||
lastCheck = Date.now();
|
||||
next();
|
||||
}, throttling - checkDiff);
|
||||
} else {
|
||||
lastCheck = nextCheck;
|
||||
next();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
module.exports.transport.checkThrottling = next => next();
|
||||
}
|
||||
|
||||
db.clearCache('sender', () => {
|
||||
callback(null, module.exports.transport);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.getTemplate = getTemplate;
|
Loading…
Add table
Add a link
Reference in a new issue