Added support for throttling

This commit is contained in:
Andris Reinman 2016-07-05 19:31:57 +03:00
parent 10bd4614ef
commit cf0042c50a
9 changed files with 67 additions and 20 deletions

View file

@ -116,7 +116,7 @@ function getTemplate(template, callback) {
}
function createMailer(callback) {
settings.list(['smtpHostname', 'smtpPort', 'smtpEncryption', 'smtpUser', 'smtpPass', 'smtpLog', 'smtpDisableAuth', 'smtpMaxConnections', 'smtpMaxMessages', 'smtpSelfSigned', 'pgpPrivateKey', 'pgpPassphrase'], (err, configItems) => {
settings.list(['smtpHostname', 'smtpPort', 'smtpEncryption', 'smtpUser', 'smtpPass', 'smtpLog', 'smtpDisableAuth', 'smtpMaxConnections', 'smtpMaxMessages', 'smtpSelfSigned', 'pgpPrivateKey', 'pgpPassphrase', 'smtpThrottling'], (err, configItems) => {
if (err) {
return callback(err);
}
@ -126,6 +126,7 @@ function createMailer(callback) {
oldListeners = module.exports.transport.listeners('idle');
module.exports.transport.removeAllListeners('idle');
module.exports.transport.removeAllListeners('stream');
module.exports.transport.checkThrottling = null;
}
module.exports.transport = nodemailer.createTransport({
@ -160,6 +161,27 @@ function createMailer(callback) {
oldListeners.forEach(listener => module.exports.transport.on('idle', listener));
}
let throttling = Number(configItems.smtpThrottling) || 0;
if (throttling) {
// convert to messages/second
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);
lastCheck = nextCheck;
if (checkDiff < throttling) {
log.verbose('Mail', 'Throttling next message in %s sec.', (throttling - checkDiff) / 1000);
setTimeout(next, throttling - checkDiff);
} else {
next();
}
};
caches.cache.delete('sender queue');
return callback(null, module.exports.transport);
});

View file

@ -26,7 +26,7 @@
"grunt": "^1.0.1",
"grunt-cli": "^1.2.0",
"grunt-contrib-nodeunit": "^1.0.0",
"grunt-eslint": "^18.1.0"
"grunt-eslint": "^19.0.0"
},
"dependencies": {
"bcrypt-nodejs": "0.0.3",
@ -41,7 +41,7 @@
"csv-parse": "^1.1.1",
"escape-html": "^1.0.3",
"express": "^4.14.0",
"express-session": "^1.13.0",
"express-session": "^1.14.0",
"faker": "^3.1.0",
"feedparser": "^1.1.4",
"geoip-ultralight": "^0.1.4",
@ -52,7 +52,7 @@
"humanize": "0.0.9",
"is-url": "^1.2.1",
"isemail": "^2.2.0",
"jsdom": "^9.3.0",
"jsdom": "^9.4.0",
"juice": "^2.0.0",
"mkdirp": "^0.5.1",
"moment-timezone": "^0.5.4",

View file

@ -12,6 +12,7 @@ $('.summernote').summernote({
$('div.code-editor').each(function () {
var editor = ace.edit(this);
var textarea = document.querySelector('input[name=html]');
editor.setTheme('ace/theme/chrome');
editor.getSession().setMode('ace/mode/html');
editor.getSession().setUseWrapMode(true);

View file

@ -11,7 +11,7 @@ let url = require('url');
let settings = require('../lib/models/settings');
let allowedKeys = ['service_url', 'smtp_hostname', 'smtp_port', 'smtp_encryption', 'smtp_disable_auth', 'smtp_user', 'smtp_pass', 'admin_email', 'smtp_log', 'smtp_max_connections', 'smtp_max_messages', 'smtp_self_signed', 'default_from', 'default_address', 'default_subject', 'default_homepage', 'default_postaddress', 'default_sender', 'verp_hostname', 'verp_use', 'disable_wysiwyg', 'pgp_private_key', 'pgp_passphrase', 'ua_code', 'shoutout', 'disable_confirmations'];
let allowedKeys = ['service_url', 'smtp_hostname', 'smtp_port', 'smtp_encryption', 'smtp_disable_auth', 'smtp_user', 'smtp_pass', 'admin_email', 'smtp_log', 'smtp_max_connections', 'smtp_max_messages', 'smtp_self_signed', 'default_from', 'default_address', 'default_subject', 'default_homepage', 'default_postaddress', 'default_sender', 'verp_hostname', 'verp_use', 'disable_wysiwyg', 'pgp_private_key', 'pgp_passphrase', 'ua_code', 'shoutout', 'disable_confirmations', 'smtp_throttling'];
router.all('/*', (req, res, next) => {
if (!req.user) {

View file

@ -65,6 +65,7 @@ router.get('/create', passport.csrfProtection, (req, res, next) => {
data.html = data.html || rendererHtml(configItems);
data.text = data.text || rendererText(configItems);
data.disableWysiwyg = configItems.disableWysiwyg;
res.render('templates/create', data);
});
});

View file

@ -430,12 +430,12 @@ let sendLoop = () => {
});
};
setImmediate(trySend);
setImmediate(getNext);
setImmediate(() => mailer.transport.checkThrottling(getNext));
});
});
};
mailer.transport.on('idle', getNext);
mailer.transport.on('idle', () => mailer.transport.checkThrottling(getNext));
});
};

View file

@ -194,6 +194,16 @@
Advanced SMTP settings
</legend>
<div class="form-group">
<div class="col-sm-offset-2 col-xs-4">
<div class="checkbox">
<label>
<input type="checkbox" name="smtp-log" {{#if smtpLog}} checked {{/if}}> Log SMTP transactions
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-xs-4">
<div class="checkbox">
@ -206,27 +216,25 @@
<div class="form-group">
<label for="smtp-max-connections" class="col-sm-2 control-label">Max connections</label>
<div class="col-sm-4">
<input type="number" class="form-control" name="smtp-max-connections" id="smtp-port" placeholder="The count of max connections, eg. 10" value="{{smtpMaxConnections}}">
<div class="col-sm-6">
<input type="number" class="form-control" name="smtp-max-connections" id="smtp-max-connections" placeholder="The count of max connections, eg. 10" value="{{smtpMaxConnections}}">
<span class="help-block">The count of maximum simultaneous connections to make against the SMTP server (defaults to 5)</span>
</div>
</div>
<div class="form-group">
<label for="smtp-max-messages" class="col-sm-2 control-label">Max messages</label>
<div class="col-sm-4">
<input type="number" class="form-control" name="smtp-max-messages" id="smtp-port" placeholder="The count of max messages, eg. 100" value="{{smtpMaxMessages}}">
<span class="help-block">The count of messages to send through a single connection before the connection is recreated (defaults to 100)</span>
<div class="col-sm-6">
<input type="number" class="form-control" name="smtp-max-messages" id="smtp-max-messages" placeholder="The count of max messages, eg. 100" value="{{smtpMaxMessages}}">
<span class="help-block">The number of messages to send through a single connection before the connection is closed and reopened (defaults to 100)</span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-xs-4">
<div class="checkbox">
<label>
<input type="checkbox" name="smtp-log" {{#if smtpLog}} checked {{/if}}> Log SMTP transactions
</label>
</div>
<label for="smtp-throttling" class="col-sm-2 control-label">Throttling</label>
<div class="col-sm-6">
<input type="number" class="form-control" name="smtp-throttling" id="smtp-throttling" placeholder="Messages per hour eg. 1000" value="{{smtpThrottling}}">
<span class="help-block">Maximum number of messages to send in an hour. Leave empty or zero for no throttling. If your provider uses a different speed limit (<em>messages/minute</em> or <em>messages/second</em>) then convert this limit into <em>messages/hour</em> (1m/s => 3600m/h).</span>
</div>
</div>
</fieldset>

View file

@ -22,6 +22,7 @@
<div class="col-sm-offset-2 col-sm-10">
<a class="btn btn-default" role="button" data-toggle="collapse" href="#mergeReference" aria-expanded="false" aria-controls="mergeReference">Merge tag reference</a>
<div class="collapse" id="mergeReference">
<p>
Merge tags are tags that are replaced before sending out the message. The format of the merge tag is the following: <code>[TAG_NAME]</code> or <code>[TAG_NAME/fallback]</code> where <code>fallback</code> is an optional text value used
when <code>TAG_NAME</code> is empty.
@ -69,12 +70,14 @@
<div class="form-group">
<label for="template-html" class="col-sm-2 control-label">Template content (HTML)</label>
<div class="col-sm-10">
{{#if disableWysiwyg}}
<div class="code-editor" id="template-html">{{html}}</div>
<input type="hidden" name="html">
{{else}}
<textarea class="form-control summernote" id="template-html" name="html" rows="8">{{html}}</textarea>
{{/if}}
</div>
</div>

View file

@ -34,6 +34,9 @@
</p>
<ul>
<li>
<code>[EMAIL]</code> email address of the subscriber
</li>
<li>
<code>[FIRST_NAME]</code> first name of the subscriber
</li>
@ -52,6 +55,15 @@
<li>
<code>[LINK_BROWSER]</code> URL to preview the message in a browser
</li>
<li>
<code>[SUBSCRIPTION_ID]</code> Unique ID that identifies the recipient
</li>
<li>
<code>[LIST_ID]</code> Unique ID that identifies the list used for this campaign
</li>
<li>
<code>[CAMPAIGN_ID]</code> Unique ID that identifies current campaign
</li>
</ul>
<p>
In addition to that any custom field can have its own merge tag.