Fixes in reports (generating a CSV).
Added caching of generated images in mosaico handler. Various other fixes.
This commit is contained in:
parent
055c4c6b51
commit
66702b5edc
39 changed files with 545 additions and 278 deletions
|
@ -6,6 +6,7 @@ const net = require('net');
|
|||
const campaigns = require('../models/campaigns');
|
||||
const contextHelpers = require('../lib/context-helpers');
|
||||
const { SubscriptionStatus } = require('../../shared/lists');
|
||||
const bluebird = require('bluebird');
|
||||
|
||||
const seenIds = new Set();
|
||||
|
||||
|
@ -33,7 +34,7 @@ async function readNextChunks() {
|
|||
try {
|
||||
const match = /\bstatus=(bounced|sent)\b/.test(line) && line.match(/\bpostfix\/\w+\[\d+\]:\s*([^:]+).*?status=(\w+)/);
|
||||
if (match) {
|
||||
let queueId = match[1];
|
||||
const queueId = match[1];
|
||||
let queued = '';
|
||||
let queuedAs = '';
|
||||
|
||||
|
@ -41,7 +42,7 @@ async function readNextChunks() {
|
|||
seenIds.add(queueId);
|
||||
|
||||
// Losacno: Check for local requeue
|
||||
let status = match[2];
|
||||
const status = match[2];
|
||||
log.verbose('POSTFIXBOUNCE', 'Checking message %s for local requeue (status: %s)', queueId, status);
|
||||
if (status === 'sent') {
|
||||
// Save new queueId to update message's previous queueId (thanks @mfechner )
|
||||
|
@ -82,7 +83,7 @@ async function readNextChunks() {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = callback => {
|
||||
function spawn(callback) {
|
||||
if (!config.postfixbounce.enabled) {
|
||||
return setImmediate(callback);
|
||||
}
|
||||
|
@ -122,4 +123,7 @@ module.exports = callback => {
|
|||
log.info('POSTFIXBOUNCE', 'Server listening on port %s', config.postfixbounce.port);
|
||||
setImmediate(callback);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports.spawn = bluebird.promisify(spawn);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ const config = require('config');
|
|||
const crypto = require('crypto');
|
||||
const humanize = require('humanize');
|
||||
const http = require('http');
|
||||
const bluebird = require('bluebird');
|
||||
|
||||
const SMTPServer = require('smtp-server').SMTPServer;
|
||||
const simpleParser = require('mailparser').simpleParser;
|
||||
|
@ -22,7 +23,7 @@ const mailstore = {
|
|||
},
|
||||
getMail(address, callback) {
|
||||
if (!this.accounts[address] || this.accounts[address].length === 0) {
|
||||
let err = new Error('No mail for ' + address);
|
||||
const err = new Error('No mail for ' + address);
|
||||
err.status = 404;
|
||||
return callback(err);
|
||||
}
|
||||
|
@ -55,8 +56,8 @@ const server = new SMTPServer({
|
|||
|
||||
// Setup authentication
|
||||
onAuth: (auth, session, callback) => {
|
||||
let username = config.testServer.username;
|
||||
let password = config.testServer.password;
|
||||
const username = config.testServer.username;
|
||||
const password = config.testServer.password;
|
||||
|
||||
// check username and password
|
||||
if (auth.username === username && auth.password === password) {
|
||||
|
@ -80,15 +81,13 @@ const server = new SMTPServer({
|
|||
// Validate RCPT TO envelope address. Example allows all addresses that do not start with 'deny'
|
||||
// If this method is not set, all addresses are allowed
|
||||
onRcptTo: (address, session, callback) => {
|
||||
let err;
|
||||
|
||||
if (/^deny/i.test(address.address)) {
|
||||
return callback(new Error('Not accepted'));
|
||||
}
|
||||
|
||||
// Reject messages larger than 100 bytes to an over-quota user
|
||||
if (/^full/i.test(address.address) && Number(session.envelope.mailFrom.args.SIZE) > 100) {
|
||||
err = new Error('Insufficient channel storage: ' + address.address);
|
||||
const err = new Error('Insufficient channel storage: ' + address.address);
|
||||
err.responseCode = 452;
|
||||
return callback(err);
|
||||
}
|
||||
|
@ -98,7 +97,7 @@ const server = new SMTPServer({
|
|||
|
||||
// Handle message stream
|
||||
onData: (stream, session, callback) => {
|
||||
let hash = crypto.createHash('md5');
|
||||
const hash = crypto.createHash('md5');
|
||||
let message = '';
|
||||
stream.on('data', chunk => {
|
||||
hash.update(chunk);
|
||||
|
@ -107,9 +106,8 @@ const server = new SMTPServer({
|
|||
}
|
||||
});
|
||||
stream.on('end', () => {
|
||||
let err;
|
||||
if (stream.sizeExceeded) {
|
||||
err = new Error('Error: message exceeds fixed maximum message size 10 MB');
|
||||
const err = new Error('Error: message exceeds fixed maximum message size 10 MB');
|
||||
err.responseCode = 552;
|
||||
return callback(err);
|
||||
}
|
||||
|
@ -129,15 +127,15 @@ server.on('error', err => {
|
|||
log.error('Test SMTP', err.stack);
|
||||
});
|
||||
|
||||
let mailBoxServer = http.createServer((req, res) => {
|
||||
let renderer = data => (
|
||||
const mailBoxServer = http.createServer((req, res) => {
|
||||
const renderer = data => (
|
||||
'<!doctype html><html><head><title>' + data.title + '</title></head><body>' + data.body + '</body></html>'
|
||||
);
|
||||
|
||||
let address = req.url.substring(1);
|
||||
const address = req.url.substring(1);
|
||||
mailstore.getMail(address, (err, mail) => {
|
||||
if (err) {
|
||||
let html = renderer({
|
||||
const html = renderer({
|
||||
title: 'error',
|
||||
body: err.message || err
|
||||
});
|
||||
|
@ -155,7 +153,7 @@ let mailBoxServer = http.createServer((req, res) => {
|
|||
delete mail.textAsHtml;
|
||||
delete mail.attachments;
|
||||
|
||||
let script = '<script> var mailObject = ' + JSON.stringify(mail) + '; console.log(mailObject); </script>';
|
||||
const script = '<script> var mailObject = ' + JSON.stringify(mail) + '; console.log(mailObject); </script>';
|
||||
html = html.replace(/<\/body\b/i, match => script + match);
|
||||
html = html.replace(/target="_blank"/g, 'target="_self"');
|
||||
|
||||
|
@ -168,7 +166,7 @@ mailBoxServer.on('error', err => {
|
|||
log.error('Test SMTP Mailbox Server', err);
|
||||
});
|
||||
|
||||
module.exports = callback => {
|
||||
function spawn(callback) {
|
||||
if (config.testServer.enabled) {
|
||||
server.listen(config.testServer.port, config.testServer.host, () => {
|
||||
log.info('Test SMTP', 'Server listening on port %s', config.testServer.port);
|
||||
|
@ -194,4 +192,6 @@ module.exports = callback => {
|
|||
} else {
|
||||
setImmediate(callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports.spawn = bluebird.promisify(spawn);
|
||||
|
|
|
@ -7,6 +7,7 @@ const {MailerError} = require('../lib/mailers');
|
|||
const campaigns = require('../models/campaigns');
|
||||
const contextHelpers = require('../lib/context-helpers');
|
||||
const {SubscriptionStatus} = require('../../shared/lists');
|
||||
const bluebird = require('bluebird');
|
||||
|
||||
const BounceHandler = require('bounce-handler').BounceHandler;
|
||||
const SMTPServer = require('smtp-server').SMTPServer;
|
||||
|
@ -85,7 +86,7 @@ const server = new SMTPServer({
|
|||
onData: onData
|
||||
});
|
||||
|
||||
module.exports = callback => {
|
||||
function spawn(callback) {
|
||||
if (!config.verp.enabled) {
|
||||
return setImmediate(callback);
|
||||
}
|
||||
|
@ -131,7 +132,7 @@ module.exports = callback => {
|
|||
started = true;
|
||||
return setImmediate(callback);
|
||||
}
|
||||
let host = hosts[pos++];
|
||||
const host = hosts[pos++];
|
||||
server.listen(config.verp.port, host, () => {
|
||||
if (started) {
|
||||
return server.close();
|
||||
|
@ -142,4 +143,6 @@ module.exports = callback => {
|
|||
};
|
||||
|
||||
startNextHost();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports.spawn = bluebird.promisify(spawn);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
const reports = require('../../../models/reports');
|
||||
const reportTemplates = require('../../../models/report-templates');
|
||||
const lists = require('../../../models/lists');
|
||||
const subscriptions = require('../../../models/subscriptions');
|
||||
const { SubscriptionSource, SubscriptionStatus } = require('../../../../shared/lists');
|
||||
const campaigns = require('../../../models/campaigns');
|
||||
const handlebars = require('handlebars');
|
||||
const hbs = require('hbs');
|
||||
|
@ -50,9 +50,11 @@ async function main() {
|
|||
}
|
||||
|
||||
const campaignsProxy = {
|
||||
getCampaignStatistics: reports.getCampaignStatistics,
|
||||
getCampaignOpenStatistics: reports.getCampaignOpenStatistics,
|
||||
getCampaignClickStatistics: reports.getCampaignClickStatistics,
|
||||
getCampaignLinkClickStatistics: reports.getCampaignLinkClickStatistics,
|
||||
getCampaignStatisticsStream: reports.getCampaignStatisticsStream,
|
||||
getCampaignOpenStatisticsStream: reports.getCampaignOpenStatisticsStream,
|
||||
getCampaignClickStatisticsStream: reports.getCampaignClickStatisticsStream,
|
||||
getCampaignLinkClickStatisticsStream: reports.getCampaignLinkClickStatisticsStream,
|
||||
|
@ -71,17 +73,45 @@ async function main() {
|
|||
knex,
|
||||
process,
|
||||
inputs,
|
||||
SubscriptionSource,
|
||||
SubscriptionStatus,
|
||||
|
||||
renderCsvFromStream: async (readable, opts) => {
|
||||
const stringifier = csvStringify(opts);
|
||||
|
||||
renderCsvFromStream: async (readable, opts, transform) => {
|
||||
const finished = new Promise((success, fail) => {
|
||||
stringifier.on('finish', () => success())
|
||||
stringifier.on('error', (err) => fail(err))
|
||||
});
|
||||
let lastReadable = readable;
|
||||
|
||||
stringifier.pipe(process.stdout);
|
||||
readable.pipe(stringifier);
|
||||
const stringifier = csvStringify(opts);
|
||||
|
||||
stringifier.on('finish', () => success());
|
||||
stringifier.on('error', err => fail(err));
|
||||
|
||||
if (transform) {
|
||||
const rowTransform = new stream.Transform({
|
||||
objectMode: true,
|
||||
transform(row, encoding, callback) {
|
||||
async function performTransform() {
|
||||
try {
|
||||
const newRow = await transform(row, encoding);
|
||||
callback(null, newRow);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
performTransform();
|
||||
}
|
||||
});
|
||||
|
||||
lastReadable.on('error', err => fail(err));
|
||||
lastReadable.pipe(rowTransform);
|
||||
|
||||
lastReadable = rowTransform;
|
||||
}
|
||||
|
||||
stringifier.pipe(process.stdout);
|
||||
lastReadable.pipe(stringifier);
|
||||
});
|
||||
|
||||
await finished;
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue