From 8546040e975db15cb06d914e73ae9fe01ec4d07e Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Thu, 26 May 2016 12:53:12 +0300 Subject: [PATCH] v1.10.1 --- CHANGELOG.txt | 4 ++++ config/default.toml | 2 +- lib/mailer.js | 21 ++++++++++++++++++--- lib/models/campaigns.js | 2 ++ package.json | 16 ++++++++-------- services/sender.js | 12 +++++------- 6 files changed, 38 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 49a5a1d6..53590b5d 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,5 +1,9 @@ # Changelog +## 1.10.1 2016-05-26 + + * Fix a bug with SMTP transport instance where campaign sending stalled until server was restarted + ## 1.10.0 2016-05-25 * Fetch multiple unsent messages at once to speed up delivery diff --git a/config/default.toml b/config/default.toml index e0dae17e..181bcc32 100644 --- a/config/default.toml +++ b/config/default.toml @@ -8,7 +8,7 @@ title="mailtrain" [log] -level="silly" +level="verbose" [www] # HTTP port to listen on diff --git a/lib/mailer.js b/lib/mailer.js index 6c76fb8f..fe27f74a 100644 --- a/lib/mailer.js +++ b/lib/mailer.js @@ -6,6 +6,7 @@ let nodemailer = require('nodemailer'); let openpgpEncrypt = require('nodemailer-openpgp').openpgpEncrypt; let settings = require('./models/settings'); let tools = require('./tools'); +let caches = require('./caches'); let Handlebars = require('handlebars'); let fs = require('fs'); let path = require('path'); @@ -102,6 +103,14 @@ function createMailer(callback) { 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 = nodemailer.createTransport({ pool: true, host: configItems.smtpHostname, @@ -114,9 +123,9 @@ function createMailer(callback) { }, debug: !!configItems.smtpLog, logger: !configItems.smtpLog ? false : { - debug: log.info.bind(log, 'Mail'), - info: log.verbose.bind(log, 'Mail'), - error: log.info.bind(log, 'Mail') + debug: log.verbose.bind(log, 'Mail'), + info: log.info.bind(log, 'Mail'), + error: log.error.bind(log, 'Mail') }, maxConnections: Number(configItems.smtpMaxConnections), maxMessages: Number(configItems.smtpMaxMessages), @@ -129,6 +138,12 @@ function createMailer(callback) { passphrase: configItems.pgpPassphrase })); + if (oldListeners.length) { + log.info('Mail', 'Reattaching %s idle listeners', oldListeners.length); + oldListeners.forEach(listener => module.exports.transport.on('idle', listener)); + } + + caches.cache.delete('sender queue'); return callback(null, module.exports.transport); }); } diff --git a/lib/models/campaigns.js b/lib/models/campaigns.js index 4e0fae42..7fc92066 100644 --- a/lib/models/campaigns.js +++ b/lib/models/campaigns.js @@ -745,6 +745,8 @@ module.exports.delete = (id, callback) => { if (err) { return callback(err); } + + caches.cache.delete('sender queue'); return callback(null, affected); }); }); diff --git a/package.json b/package.json index 5eb2d747..f9c26462 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mailtrain", "private": true, - "version": "1.10.0", + "version": "1.10.1", "description": "Self hosted email newsletter app", "main": "index.js", "scripts": { @@ -33,10 +33,10 @@ "body-parser": "^1.15.1", "bounce-handler": "^7.3.2-fork.0", "compression": "^1.6.2", - "config": "^1.20.1", + "config": "^1.20.4", "connect-flash": "^0.1.1", "connect-redis": "^3.0.2", - "cookie-parser": "^1.4.1", + "cookie-parser": "^1.4.2", "csurf": "^1.8.3", "csv-parse": "^1.1.0", "escape-html": "^1.0.3", @@ -50,16 +50,16 @@ "html-to-text": "^2.1.0", "humanize": "0.0.9", "is-url": "^1.2.1", - "isemail": "^2.1.0", - "jsdom": "^9.0.0", - "juice": "^1.11.0", + "isemail": "^2.1.2", + "jsdom": "^9.2.0", + "juice": "^2.0.0", "moment-timezone": "^0.5.4", "morgan": "^1.7.0", "multer": "^1.1.0", "mysql": "^2.10.2", - "nodemailer": "^2.4.1", + "nodemailer": "^2.4.2", "nodemailer-openpgp": "^1.0.2", - "npmlog": "^2.0.3", + "npmlog": "^2.0.4", "openpgp": "^2.3.0", "passport": "^0.3.2", "passport-local": "^1.0.0", diff --git a/services/sender.js b/services/sender.js index 19506e10..204f24a8 100644 --- a/services/sender.js +++ b/services/sender.js @@ -18,7 +18,6 @@ let request = require('request'); let caches = require('../lib/caches'); function findUnsent(callback) { - let returnUnsent = (row, campaign) => { db.getConnection((err, connection) => { if (err) { @@ -59,7 +58,6 @@ function findUnsent(callback) { // Find "normal" campaigns. Ignore RSS and drip campaigns at this point let query = 'SELECT `id`, `list`, `segment` FROM `campaigns` WHERE `status`=? AND (`scheduled` IS NULL OR `scheduled` <= NOW()) AND `type` IN (?, ?) LIMIT 1'; - connection.query(query, [2, 1, 3], (err, rows) => { connection.release(); if (err) { @@ -79,6 +77,7 @@ function findUnsent(callback) { values: [] }); } + segments.getQuery(segmentId, 'subscription', next); }; @@ -100,14 +99,15 @@ function findUnsent(callback) { let values; // NOT IN - query = 'SELECT * FROM `subscription__' + campaign.list + '` AS subscription WHERE status=1 ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' AND id NOT IN (SELECT subscription FROM `campaign__' + campaign.id + '` campaign WHERE campaign.list = ? AND campaign.segment = ? AND campaign.subscription = subscription.id) LIMIT 100'; + query = 'SELECT * FROM `subscription__' + campaign.list + '` AS subscription WHERE status=1 ' + (queryData.where ? ' AND (' + queryData.where + ')' : '') + ' AND id NOT IN (SELECT subscription FROM `campaign__' + campaign.id + '` campaign WHERE campaign.list = ? AND campaign.segment = ? AND campaign.subscription = subscription.id) LIMIT 150'; values = queryData.values.concat([campaign.list, campaign.segment]); // LEFT JOIN / IS NULL - //query = 'SELECT subscription.* FROM `subscription__' + campaign.list + '` AS subscription LEFT JOIN `campaign__' + campaign.id + '` AS campaign ON campaign.list = ? AND campaign.segment = ? AND campaign.subscription = subscription.id WHERE subscription.status=1 ' + (queryData.where ? 'AND (' + queryData.where + ') ' : '') + 'AND campaign.id IS NULL LIMIT 100'; + //query = 'SELECT subscription.* FROM `subscription__' + campaign.list + '` AS subscription LEFT JOIN `campaign__' + campaign.id + '` AS campaign ON campaign.list = ? AND campaign.segment = ? AND campaign.subscription = subscription.id WHERE subscription.status=1 ' + (queryData.where ? 'AND (' + queryData.where + ') ' : '') + 'AND campaign.id IS NULL LIMIT 150'; //values = [campaign.list, campaign.segment].concat(queryData.values); connection.query(query, values, (err, rows) => { + if (err) { connection.release(); return callback(err); @@ -146,7 +146,6 @@ function formatMessage(message, callback) { if (!campaign) { return callback(new Error('Campaign not found')); } - lists.get(message.listId, (err, list) => { if (err) { return callback(err); @@ -161,7 +160,6 @@ function formatMessage(message, callback) { } let useVerp = config.verp.enabled && configItems.verpUse && configItems.verpHostname; - fields.list(list.id, (err, fieldList) => { if (err) { return callback(err); @@ -274,6 +272,7 @@ function formatMessage(message, callback) { Object.keys(message.subscription.mergeTags).forEach(key => { form[key] = message.subscription.mergeTags[key]; }); + request.post({ url: campaign.sourceUrl, form @@ -321,7 +320,6 @@ let sendLoop = () => { } //log.verbose('Mail', 'Found new message to be delivered: %s', message.subscription.cid); - // format message to nodemailer message format formatMessage(message, (err, mail) => { if (err) {