diff --git a/src/mailer.js b/src/mailer.js index 4e5530695..19b6a61f9 100644 --- a/src/mailer.js +++ b/src/mailer.js @@ -20,12 +20,10 @@ exports = module.exports = { sendTestMail: sendTestMail, - _getMailQueue: _getMailQueue, - _clearMailQueue: _clearMailQueue + _mailQueue: [] // accumulate mails in test mode }; var assert = require('assert'), - async = require('async'), config = require('./config.js'), debug = require('debug')('box:mailer'), docker = require('./docker.js').connection, @@ -42,8 +40,6 @@ var NOOP_CALLBACK = function (error) { if (error) debug(error); }; var MAIL_TEMPLATES_DIR = path.join(__dirname, 'mail_templates'); -var gMailQueue = [ ]; - // This will collect the most common details required for notification emails function getMailConfig(callback) { assert.strictEqual(typeof callback, 'function'); @@ -62,18 +58,15 @@ function getMailConfig(callback) { }); } - -function processQueue() { - sendMails(gMailQueue); - gMailQueue = [ ]; -} - -// note : this function should NOT access the database. it is called by the crashnotifier -// which does not initialize mailer or the databse -function sendMails(queue, callback) { - assert(util.isArray(queue)); +function sendMail(mailOptions, callback) { + assert.strictEqual(typeof mailOptions, 'object'); callback = callback || NOOP_CALLBACK; + if (process.env.BOX_ENV === 'test') { + exports._mailQueue.push(mailOptions); + return callback(); + } + docker.getContainer('mail').inspect(function (error, data) { if (error) return callback(error); @@ -97,34 +90,16 @@ function sendMails(queue, callback) { } })); - debug('Processing mail queue of size %d (through %s:2525)', queue.length, mailServerIp); + transport.sendMail(mailOptions, function (error) { + if (error) return callback(error); - async.mapSeries(queue, function iterator(mailOptions, callback) { - transport.sendMail(mailOptions, function (error) { - if (error) return debug(error); // TODO: requeue? - debug('Email sent to ' + mailOptions.to); - }); - callback(null); - }, function done() { - debug('Done processing mail queue'); + debug(`Email "${mailOptions.subject}" sent to ${mailOptions.to}`); callback(null); }); }); } -function enqueue(mailOptions) { - assert.strictEqual(typeof mailOptions, 'object'); - - if (!mailOptions.from) debug('sender address is missing'); - if (!mailOptions.to) debug('recipient address is missing'); - - debug('Queued mail for ' + mailOptions.from + ' to ' + mailOptions.to); - gMailQueue.push(mailOptions); - - if (process.env.BOX_ENV !== 'test') processQueue(); -} - function render(templateFile, params) { assert.strictEqual(typeof templateFile, 'string'); assert.strictEqual(typeof params, 'object'); @@ -155,7 +130,7 @@ function mailUserEvent(mailTo, user, event) { text: render('user_event.ejs', { user: user, event: event, format: 'text' }), }; - enqueue(mailOptions); + sendMail(mailOptions); }); } @@ -191,7 +166,7 @@ function sendInvite(user, invitor) { html: render('welcome_user.ejs', templateDataHTML) }; - enqueue(mailOptions); + sendMail(mailOptions); }); } @@ -224,7 +199,7 @@ function userAdded(mailTo, user) { html: render('user_added.ejs', templateDataHTML) }; - enqueue(mailOptions); + sendMail(mailOptions); }); } @@ -276,7 +251,7 @@ function passwordReset(user) { html: render('password_reset.ejs', templateDataHTML) }; - enqueue(mailOptions); + sendMail(mailOptions); }); } @@ -296,7 +271,7 @@ function appUp(mailTo, app) { text: render('app_up.ejs', { title: app.manifest.title, appFqdn: app.fqdn, format: 'text' }) }; - enqueue(mailOptions); + sendMail(mailOptions); }); } @@ -316,7 +291,7 @@ function appDied(mailTo, app) { text: render('app_down.ejs', { title: app.manifest.title, appFqdn: app.fqdn, format: 'text' }) }; - enqueue(mailOptions); + sendMail(mailOptions); }); } @@ -356,9 +331,7 @@ function appUpdateAvailable(mailTo, app, hasSubscription, info, callback) { html: render('app_update_available.ejs', templateDataHTML) }; - enqueue(mailOptions); - - callback(); + sendMail(mailOptions, callback); }); } @@ -391,9 +364,7 @@ function sendDigest(mailTo, info, callback) { html: render('digest.ejs', templateDataHTML) }; - enqueue(mailOptions); - - callback(); + sendMail(mailOptions, callback); }); } @@ -410,7 +381,7 @@ function backupFailed(mailTo, errorMessage, logUrl) { text: render('backup_failed.ejs', { cloudronName: mailConfig.cloudronName, message: errorMessage, logUrl: logUrl, format: 'text' }) }; - enqueue(mailOptions); + sendMail(mailOptions); }); } @@ -429,7 +400,7 @@ function certificateRenewalError(mailTo, domain, message) { text: render('certificate_renewal_error.ejs', { domain: domain, message: message, format: 'text' }) }; - sendMails([ mailOptions ]); + sendMail(mailOptions); }); } @@ -448,7 +419,7 @@ function oomEvent(mailTo, program, event) { text: render('oom_event.ejs', { cloudronName: mailConfig.cloudronName, program: program, event: JSON.stringify(event), format: 'text' }) }; - sendMails([ mailOptions ]); + sendMail(mailOptions); }); } @@ -466,16 +437,6 @@ function sendTestMail(domain, email) { text: render('test.ejs', { cloudronName: mailConfig.cloudronName, format: 'text'}) }; - enqueue(mailOptions); + sendMail(mailOptions); }); } - -function _getMailQueue() { - return gMailQueue; -} - -function _clearMailQueue(callback) { - gMailQueue = []; - - if (callback) callback(); -} diff --git a/src/routes/test/profile-test.js b/src/routes/test/profile-test.js index 7fa2e17b4..9c124a055 100644 --- a/src/routes/test/profile-test.js +++ b/src/routes/test/profile-test.js @@ -36,8 +36,6 @@ describe('Profile API', function () { server.start(function (error) { expect(!error).to.be.ok(); - mailer._clearMailQueue(); - database._clear(function (error) { expect(error).to.eql(null); @@ -61,7 +59,7 @@ describe('Profile API', function () { database._clear(function (error) { expect(!error).to.be.ok(); - mailer._clearMailQueue(); + mailer._mailQueue = []; server.stop(done); }); diff --git a/src/routes/test/users-test.js b/src/routes/test/users-test.js index 106391edb..6313e6aa1 100644 --- a/src/routes/test/users-test.js +++ b/src/routes/test/users-test.js @@ -44,10 +44,11 @@ var groupObject; function setup(done) { config._reset(); + mailer._mailQueue = []; + async.series([ server.start, database._clear, - mailer._clearMailQueue, domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), mail.addDomain.bind(null, DOMAIN_0.domain) ], function (error) { @@ -67,7 +68,7 @@ function cleanup(done) { database._clear(function (error) { expect(!error).to.be.ok(); - mailer._clearMailQueue(); + mailer._mailQueue = []; server.stop(done); }); @@ -76,8 +77,8 @@ function cleanup(done) { function checkMails(number, done) { // mails are enqueued async setTimeout(function () { - expect(mailer._getMailQueue().length).to.equal(number); - mailer._clearMailQueue(); + expect(mailer._mailQueue.length).to.equal(number); + mailer._mailQueue = []; done(); }, 500); } @@ -336,7 +337,7 @@ describe('Users API', function () { }); it('can send invite', function (done) { - mailer._clearMailQueue(); + mailer._mailQueue = []; superagent.post(SERVER_URL + '/api/v1/users/' + user_1.id + '/send_invite') .query({ access_token: token }) @@ -450,7 +451,7 @@ describe('Users API', function () { }); it('create second and third user', function (done) { - mailer._clearMailQueue(); + mailer._mailQueue = []; superagent.post(SERVER_URL + '/api/v1/users') .query({ access_token: token }) .send({ username: USERNAME_2, email: EMAIL_2 }) @@ -685,7 +686,7 @@ describe('Users API', function () { }); it('cannot create user with bad password', function (done) { - mailer._clearMailQueue(); + mailer._mailQueue = []; superagent.post(SERVER_URL + '/api/v1/users') .query({ access_token: token }) diff --git a/src/test/digest-test.js b/src/test/digest-test.js index c6d1283de..3a8bc3e27 100644 --- a/src/test/digest-test.js +++ b/src/test/digest-test.js @@ -47,13 +47,13 @@ var AUDIT_SOURCE = { function checkMails(number, email, done) { // mails are enqueued async setTimeout(function () { - expect(mailer._getMailQueue().length).to.equal(number); + expect(mailer._mailQueue.length).to.equal(number); if (number) { - expect(mailer._getMailQueue()[0].to).to.equal(email); + expect(mailer._mailQueue[0].to).to.equal(email); } - mailer._clearMailQueue(); + mailer._mailQueue = []; done(); }, 500); } @@ -67,6 +67,8 @@ describe('digest', function () { config.setFqdn(DOMAIN_0.domain); safe.fs.unlinkSync(paths.UPDATE_CHECKER_FILE); + mailer._mailQueue = []; + async.series([ database.initialize, database._clear, @@ -84,12 +86,11 @@ describe('digest', function () { }, eventlog.add.bind(null, eventlog.ACTION_UPDATE, AUDIT_SOURCE, { taskId: 12, boxUpdateInfo: { sourceTarballUrl: 'xx', version: '1.2.3', changelog: [ 'good stuff' ] } }), maildb.update.bind(null, DOMAIN_0.domain, { enabled: true }), - mailer._clearMailQueue ], done); }); after(function (done) { - mailer._clearMailQueue(); + mailer._mailQueue = []; safe.fs.unlinkSync(paths.UPDATE_CHECKER_FILE); async.series([ diff --git a/src/test/updatechecker-test.js b/src/test/updatechecker-test.js index 696ac27a8..149c2d072 100644 --- a/src/test/updatechecker-test.js +++ b/src/test/updatechecker-test.js @@ -51,14 +51,14 @@ const UPDATE_VERSION = semver.inc(config.version(), 'major'); function checkMails(number, done) { // mails are enqueued async setTimeout(function () { - expect(mailer._getMailQueue().length).to.equal(number); - mailer._clearMailQueue(); + expect(mailer._mailQueue.length).to.equal(number); + mailer._mailQueue = []; done(); }, 500); } function cleanup(done) { - mailer._clearMailQueue(); + mailer._mailQueue = []; safe.fs.unlinkSync(paths.UPDATE_CHECKER_FILE); async.series([ @@ -76,6 +76,8 @@ describe('updatechecker - box - manual (email)', function () { config.set('provider', 'notcaas'); safe.fs.unlinkSync(paths.UPDATE_CHECKER_FILE); + mailer._mailQueue = []; + async.series([ database.initialize, database._clear, @@ -85,7 +87,6 @@ describe('updatechecker - box - manual (email)', function () { users.createOwner.bind(null, USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE), settings.setBoxAutoupdatePattern.bind(null, constants.AUTOUPDATE_PATTERN_NEVER), settingsdb.set.bind(null, settings.APPSTORE_CONFIG_KEY, JSON.stringify({ userId: 'uid', cloudronId: 'cid', token: 'token' })), - mailer._clearMailQueue ], done); }); @@ -150,12 +151,13 @@ describe('updatechecker - box - automatic (no email)', function () { config.set('apiServerOrigin', 'http://localhost:4444'); config.set('provider', 'notcaas'); + mailer._mailQueue = []; + async.series([ database.initialize, cron.startPostActivationJobs, domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), mail.addDomain.bind(null, DOMAIN_0.domain), - mailer._clearMailQueue, users.createOwner.bind(null, USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE), settingsdb.set.bind(null, settings.APPSTORE_CONFIG_KEY, JSON.stringify({ userId: 'uid', cloudronId: 'cid', token: 'token' })) ], done); @@ -188,12 +190,13 @@ describe('updatechecker - box - automatic free (email)', function () { config.set('apiServerOrigin', 'http://localhost:4444'); config.set('provider', 'notcaas'); + mailer._mailQueue = []; + async.series([ database.initialize, cron.startPostActivationJobs, domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), mail.addDomain.bind(null, DOMAIN_0.domain), - mailer._clearMailQueue, users.createOwner.bind(null, USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE), settingsdb.set.bind(null, settings.APPSTORE_CONFIG_KEY, JSON.stringify({ userId: 'uid', cloudronId: 'cid', token: 'token' })) ], done); @@ -252,13 +255,14 @@ describe('updatechecker - app - manual (email)', function () { config.set('apiServerOrigin', 'http://localhost:4444'); config.set('provider', 'notcaas'); + mailer._mailQueue = []; + async.series([ database.initialize, database._clear, cron.startPostActivationJobs, domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), mail.addDomain.bind(null, DOMAIN_0.domain), - mailer._clearMailQueue, function (next) { users.createOwner(USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE, function (error, userObject) { if (error) return next(error); @@ -375,13 +379,14 @@ describe('updatechecker - app - automatic (no email)', function () { config.set('apiServerOrigin', 'http://localhost:4444'); config.set('provider', 'notcaas'); + mailer._mailQueue = []; + async.series([ database.initialize, database._clear, cron.startPostActivationJobs, domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), mail.addDomain.bind(null, DOMAIN_0.domain), - mailer._clearMailQueue, function (next) { users.createOwner(USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE, function (error, userObject) { if (error) return next(error); @@ -448,13 +453,14 @@ describe('updatechecker - app - automatic free (email)', function () { config.set('apiServerOrigin', 'http://localhost:4444'); config.set('provider', 'notcaas'); + mailer._mailQueue = []; + async.series([ database.initialize, database._clear, cron.startPostActivationJobs, domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), mail.addDomain.bind(null, DOMAIN_0.domain), - mailer._clearMailQueue, function (next) { users.createOwner(USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE, function (error, userObject) { if (error) return next(error); diff --git a/src/test/users-test.js b/src/test/users-test.js index b54e43c34..1f6b3fa8f 100644 --- a/src/test/users-test.js +++ b/src/test/users-test.js @@ -49,11 +49,12 @@ const DOMAIN_0 = { }; function cleanupUsers(done) { + mailer._mailQueue = []; + async.series([ groupdb._clear, userdb._clear, mailboxdb._clear, - mailer._clearMailQueue ], done); } @@ -72,17 +73,18 @@ function setup(done) { config._reset(); config.setFqdn(DOMAIN_0.domain); + mailer._mailQueue = []; + async.series([ database.initialize, database._clear, domains.add.bind(null, DOMAIN_0.domain, DOMAIN_0, AUDIT_SOURCE), mail.addDomain.bind(null, DOMAIN_0.domain), - mailer._clearMailQueue ], done); } function cleanup(done) { - mailer._clearMailQueue(); + mailer._mailQueue = []; async.series([ database._clear, @@ -98,11 +100,11 @@ function checkMails(number, options, callback) { // mails are enqueued async setTimeout(function () { - expect(mailer._getMailQueue().length).to.equal(number); + expect(mailer._mailQueue.length).to.equal(number); - if (options && options.sentTo) expect(mailer._getMailQueue().some(function (mail) { return mail.to === options.sentTo; })); + if (options && options.sentTo) expect(mailer._mailQueue.some(function (mail) { return mail.to === options.sentTo; })); - mailer._clearMailQueue(); + mailer._mailQueue = []; callback(); }, 500);