diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index a29357aa..49a5a1d6 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,5 +1,11 @@
# Changelog
+## 1.10.0 2016-05-25
+
+ * Fetch multiple unsent messages at once to speed up delivery
+ * Fixed a bug of counting unsubscribers correctly
+ * Use LONGTEXT for template text fields (messages might include inlined images which are larger than 64kB)
+
## 1.9.0 2016-05-16
* New look
diff --git a/lib/caches.js b/lib/caches.js
new file mode 100644
index 00000000..b7c308ba
--- /dev/null
+++ b/lib/caches.js
@@ -0,0 +1,28 @@
+'use strict';
+
+let cache = module.exports.cache = new Map();
+
+module.exports.push = (name, value) => {
+ if (!cache.has(name)) {
+ cache.set(name, []);
+ } else if (!Array.isArray(cache.get(name))) {
+ cache.set(name, [].concat(cache.get(name) || []));
+ }
+ cache.get(name).push(value);
+};
+
+module.exports.shift = name => {
+ if (!cache.has(name)) {
+ return false;
+ }
+ if (!Array.isArray(cache.get(name))) {
+ let value = cache.get(name);
+ cache.delete(name);
+ return value;
+ }
+ let value = cache.get(name).shift();
+ if (!cache.get(name).length) {
+ cache.delete(name);
+ }
+ return value;
+};
diff --git a/lib/models/campaigns.js b/lib/models/campaigns.js
index 34816e73..4e0fae42 100644
--- a/lib/models/campaigns.js
+++ b/lib/models/campaigns.js
@@ -11,6 +11,7 @@ let isUrl = require('is-url');
let feed = require('../feed');
let log = require('npmlog');
let mailer = require('../mailer');
+let caches = require('../caches');
let allowedKeys = ['description', 'from', 'address', 'subject', 'template', 'source_url', 'list', 'segment', 'html', 'text'];
@@ -809,6 +810,7 @@ module.exports.pause = (id, callback) => {
connection.release();
return callback(err);
}
+ caches.cache.delete('sender queue');
return callback(null, true);
});
});
@@ -821,7 +823,7 @@ module.exports.reset = (id, callback) => {
return callback(err);
}
- if (campaign.status !== 3 && !(campaign.status === 2 && campaign.scheduled > new Date())) {
+ if (campaign.status !== 3 && campaign.status !== 4 && !(campaign.status === 2 && campaign.scheduled > new Date())) {
return callback(null, false);
}
@@ -835,6 +837,8 @@ module.exports.reset = (id, callback) => {
connection.release();
return callback(err);
}
+
+ caches.cache.delete('sender queue');
connection.query('DELETE FROM links WHERE campaign=?', [id], err => {
if (err) {
connection.release();
diff --git a/meta.json b/meta.json
index 43faa406..bca525e4 100644
--- a/meta.json
+++ b/meta.json
@@ -1,3 +1,3 @@
{
- "schemaVersion": 12
+ "schemaVersion": 13
}
diff --git a/package.json b/package.json
index 9ee31c0c..5eb2d747 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "mailtrain",
"private": true,
- "version": "1.9.0",
+ "version": "1.10.0",
"description": "Self hosted email newsletter app",
"main": "index.js",
"scripts": {
diff --git a/services/sender.js b/services/sender.js
index 1e10e7e3..19506e10 100644
--- a/services/sender.js
+++ b/services/sender.js
@@ -15,9 +15,7 @@ let shortid = require('shortid');
let url = require('url');
let htmlToText = require('html-to-text');
let request = require('request');
-
-// to speed things up fetch several unsent messages and store these into a cache
-let fetchCache = [];
+let caches = require('../lib/caches');
function findUnsent(callback) {
@@ -49,8 +47,8 @@ function findUnsent(callback) {
});
};
- if (fetchCache.length) {
- let cached = fetchCache.shift();
+ if (caches.cache.has('sender queue')) {
+ let cached = caches.shift('sender queue');
return returnUnsent(cached.row, cached.campaign);
}
@@ -125,7 +123,7 @@ function findUnsent(callback) {
connection.release();
rows.forEach(row => {
- fetchCache.push({
+ caches.push('sender queue', {
row,
campaign
});
diff --git a/setup/sql/mailtrain.sql b/setup/sql/mailtrain.sql
index fe1adaa0..ecd895f8 100644
--- a/setup/sql/mailtrain.sql
+++ b/setup/sql/mailtrain.sql
@@ -14,7 +14,9 @@ CREATE TABLE `campaign` (
PRIMARY KEY (`id`),
UNIQUE KEY `list` (`list`,`segment`,`subscription`),
KEY `created` (`created`),
- KEY `response_id` (`response_id`)
+ KEY `response_id` (`response_id`),
+ KEY `status_index` (`status`),
+ KEY `subscription_index` (`subscription`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `campaign_tracker` (
`list` int(11) unsigned NOT NULL,
@@ -43,9 +45,9 @@ CREATE TABLE `campaigns` (
`from` varchar(255) DEFAULT '',
`address` varchar(255) DEFAULT '',
`subject` varchar(255) DEFAULT '',
- `html` text,
- `html_prepared` text,
- `text` text,
+ `html` longtext,
+ `html_prepared` longtext,
+ `text` longtext,
`status` tinyint(4) unsigned NOT NULL DEFAULT '1',
`scheduled` timestamp NULL DEFAULT NULL,
`status_change` timestamp NULL DEFAULT NULL,
@@ -174,7 +176,7 @@ CREATE TABLE `segments` (
`created` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `list` (`list`),
- KEY `name` (`name`(191)),
+ KEY `name` (`name`),
CONSTRAINT `segments_ibfk_1` FOREIGN KEY (`list`) REFERENCES `lists` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `settings` (
@@ -183,7 +185,7 @@ CREATE TABLE `settings` (
`value` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `key` (`key`)
-) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4;
+) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4;
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (1,'smtp_hostname','localhost');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (2,'smtp_port','465');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (3,'smtp_encryption','TLS');
@@ -200,7 +202,7 @@ INSERT INTO `settings` (`id`, `key`, `value`) VALUES (13,'default_from','My Awes
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (14,'default_address','admin@example.com');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (15,'default_subject','Test message');
INSERT INTO `settings` (`id`, `key`, `value`) VALUES (16,'default_homepage','http://localhost:3000/');
-INSERT INTO `settings` (`id`, `key`, `value`) VALUES (17,'db_schema_version','10');
+INSERT INTO `settings` (`id`, `key`, `value`) VALUES (17,'db_schema_version','13');
CREATE TABLE `subscription` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`cid` varchar(255) CHARACTER SET ascii NOT NULL,
@@ -228,8 +230,8 @@ CREATE TABLE `templates` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
`description` text,
- `html` text,
- `text` text,
+ `html` longtext,
+ `text` longtext,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `name` (`name`(191))
diff --git a/setup/sql/upgrade-00013.sql b/setup/sql/upgrade-00013.sql
new file mode 100644
index 00000000..288e8e21
--- /dev/null
+++ b/setup/sql/upgrade-00013.sql
@@ -0,0 +1,15 @@
+# Header section
+# Define incrementing schema version number
+SET @schema_version = '13';
+
+-- {{#each tables.campaign}}
+
+ # Adds separate index for 'subscription' on campaign messages table
+ CREATE INDEX subscription_index ON `{{this}}` (`subscription`);
+
+-- {{/each}}
+
+# Footer section
+LOCK TABLES `settings` WRITE;
+INSERT INTO `settings` (`key`, `value`) VALUES('db_schema_version', @schema_version) ON DUPLICATE KEY UPDATE `value`=@schema_version;
+UNLOCK TABLES;
diff --git a/views/campaigns/view.hbs b/views/campaigns/view.hbs
index cbe84648..c736592c 100644
--- a/views/campaigns/view.hbs
+++ b/views/campaigns/view.hbs
@@ -210,13 +210,21 @@
{{#if isPaused}}
-
+
+
+
+
+
+
Sending paused
{{/if}}
diff --git a/views/lists/view.hbs b/views/lists/view.hbs
index 9dd0a5ee..c535b167 100644
--- a/views/lists/view.hbs
+++ b/views/lists/view.hbs
@@ -61,140 +61,141 @@
+ {{#if showSubscriptions}}
+
-
+
+
+
+
+
+ #
+ |
+
+ Address
+ |
+
+ First Name
+ |
+
+ Last Name
+ |
+ {{#each customFields}}
+
+ {{name}}
+ |
+ {{/each}}
+
+ Status
+ |
+ |
+
+
+
+
-
-
-
-
+ {{/if}}
+
+
+ {{#if showImports}}
+
+
+
+
+
#
|
- Address
+ Created
|
- First Name
+ Finished
|
- Last Name
+ Type
+ |
+
+ Added
+ |
+
+ Updated
+ |
+
+ Failed
|
- {{#each customFields}}
-
- {{name}}
- |
- {{/each}}
Status
|
- |
-
-
-
-
+
-
-
+ |
+
+
+ {{#if imports}}
+ {{#each imports}}
+
+
+ {{index}}
+ |
-
+
+ {{created}}
+ |
-
-
-
-
- #
- |
-
- Created
- |
-
- Finished
- |
-
- Type
- |
-
- Added
- |
-
- Updated
- |
-
- Failed
- |
-
- Status
- |
-
+ |
+ {{#if finished}}
+ {{finished}}
+ {{else}}
+ No
+ {{/if}}
+ |
-
-
-
- {{#if imports}}
- {{#each imports}}
+
+ {{importType}}
+ |
+
+ {{new}}
+ |
+
+ {{updated}}
+ |
+
+ {{#if failed}}{{failed}}{{else}}0 {{/if}}
+ |
+
+ {{#if error}}
+ {{importStatus}}
+ {{else}}
+ {{importStatus}}
+ {{/if}}
+ |
+
+
+ |
+
+ {{/each}}
+ {{else}}
-
- {{index}}
- |
-
-
- {{created}}
- |
-
-
- {{#if finished}}
- {{finished}}
- {{else}}
- No
- {{/if}}
- |
-
-
- {{importType}}
- |
-
- {{new}}
- |
-
- {{updated}}
- |
-
- {{#if failed}}{{failed}}{{else}}0 {{/if}}
- |
-
- {{#if error}}
- {{importStatus}}
- {{else}}
- {{importStatus}}
- {{/if}}
- |
-
-
+ |
+ No data available in table
|
- {{/each}}
- {{else}}
-
-
- No data available in table
- |
-
- {{/if}}
-
-
-
-
+ {{/if}}
+
+
+
+ {{/if}}