mail-time 1.3.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +520 -145
- package/adapters/blank-example.js +230 -0
- package/adapters/mongo.js +310 -0
- package/adapters/redis.js +302 -0
- package/helpers.js +11 -0
- package/index.cjs +1285 -0
- package/index.js +428 -383
- package/package.json +30 -11
- package/CHANGELOG.md +0 -1
- package/HISTORY.md +0 -1
package/index.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let equals;
|
|
21
|
-
equals = (a, b) => {
|
|
1
|
+
import { JoSk, RedisAdapter, MongoAdapter } from 'josk';
|
|
2
|
+
import merge from 'deepmerge';
|
|
3
|
+
|
|
4
|
+
import crypto from 'crypto';
|
|
5
|
+
import { MongoQueue } from './adapters/mongo.js';
|
|
6
|
+
import { RedisQueue } from './adapters/redis.js';
|
|
7
|
+
import { debug, logError } from './helpers.js';
|
|
8
|
+
|
|
9
|
+
const noop = () => {};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if entities of various types are equal
|
|
13
|
+
* including edge cases like unordered Objects and Array
|
|
14
|
+
* @function equals
|
|
15
|
+
* @param {mix} a
|
|
16
|
+
* @param {mix} b
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*/
|
|
19
|
+
const equals = (a, b) => {
|
|
22
20
|
let i;
|
|
23
21
|
if (a === b) {
|
|
24
22
|
return true;
|
|
@@ -92,39 +90,82 @@ equals = (a, b) => {
|
|
|
92
90
|
|
|
93
91
|
let DEFAULT_TEMPLATE = '<!DOCTYPE html><html xmlns=http://www.w3.org/1999/xhtml><meta content="text/html; charset=utf-8"http-equiv=Content-Type><meta content="width=device-width,initial-scale=1"name=viewport><title>{{subject}}</title><style>body{-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:none;font-family:Tiempos,Georgia,Times,serif;font-weight:400;width:100%;height:100%;background:#fff;font-size:15px;color:#000;line-height:1.5}a{text-decoration:underline;border:0;color:#000;outline:0;color:inherit}a:hover{text-decoration:none}a[href^=sms],a[href^=tel]{text-decoration:none;color:#000;cursor:default}a img{border:none;text-decoration:none}td{font-family:Tiempos,Georgia,Times,serif;font-weight:400}hr{height:1px;border:none;width:100%;margin:0;margin-top:25px;margin-bottom:25px;background-color:#ECECEC}h1,h2,h3,h4,h5,h6{font-family:HelveticaNeue,"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;line-height:normal;margin-top:35px;margin-bottom:4px;margin-left:0;margin-right:0}h1{margin:23px 15px;font-size:25px}h2{margin-top:15px;font-size:21px}h3{font-weight:400;font-size:19px;border-bottom:1px solid #ECECEC}h4{font-weight:400;font-size:18px}h5{font-weight:400;font-size:17px}h6{font-weight:600;font-size:16px}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{text-decoration:none}pre{font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif;display:block;font-size:13px;padding:9.5px;margin:0 0 10px;line-height:1.42;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px;text-align:left!important;max-width:100%;white-space:pre-wrap;width:auto;overflow:auto}code{font-size:13px;font-family:font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif;border:1px solid rgba(0,0,0,.223);border-radius:2px;padding:1px 2px;word-break:break-all;word-wrap:break-word}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border:none;border-radius:0;word-break:break-all;word-wrap:break-word}td{text-align:center}table{border-collapse:collapse!important}.force-full-width{width:100%!important}</style><style media=screen>@media screen{h1,h2,h3,h4,h5,h6{font-family:\'Helvetica Neue\',Arial,sans-serif!important}td{font-family:Tiempos,Georgia,Times,serif!important}code,pre{font-family:Consolas,Menlo,Monaco,\'Lucida Console\',\'Liberation Mono\',\'DejaVu Sans Mono\',\'Bitstream Vera Sans Mono\',\'Courier New\',monospace,sans-serif!important}}</style><style media="only screen and (max-width:480px)">@media only screen and (max-width:480px){table[class=w320]{width:100%!important}}</style><body bgcolor=#FFFFFF class=body style=padding:0;margin:0;display:block;background:#fff;-webkit-text-size-adjust:none><table cellpadding=0 cellspacing=0 width=100% align=center><tr><td align=center valign=top bgcolor=#FFFFFF width=100%><center><table cellpadding=0 cellspacing=0 width=600 style="margin:0 auto"class=w320><tr><td align=center valign=top><table cellpadding=0 cellspacing=0 width=100% style="margin:0 auto;border-bottom:1px solid #ddd"bgcolor=#ECECEC><tr><td><h1>{{{subject}}}</h1></table><table cellpadding=0 cellspacing=0 width=100% style="margin:0 auto"bgcolor=#F2F2F2><tr><td><center><table cellpadding=0 cellspacing=0 width=100% style="margin:0 auto"><tr><td align=left style="text-align:left;padding:30px 25px">{{{html}}}</table></center></table></table></center></table>';
|
|
94
92
|
|
|
95
|
-
|
|
93
|
+
/** Class of MailTime */
|
|
94
|
+
class MailTime {
|
|
95
|
+
/**
|
|
96
|
+
* Create a MailTime instance
|
|
97
|
+
* @param {object} opts - configuration object
|
|
98
|
+
* @param {RedisQueue|MongoQueue|CustomQueue} opts.queue - Queue Storage Driver instance
|
|
99
|
+
* @param {string} [opts.type] - "server" or "client" type of MailTime instance
|
|
100
|
+
* @param {function} [opts.from] - A function returning *String* for `from` header, format: `"MyApp" <user@example.com>`
|
|
101
|
+
* @param {[object]} [opts.transports] - An array of `nodemailer`'s transports, returned from `nodemailer.createTransport({})`; Required for `{type: 'server'}`
|
|
102
|
+
* @param {string} [opts.strategy] - `backup` or `balancer`
|
|
103
|
+
* @param {number} [opts.failsToNext] - After how many failed "send attempts" switch to the next transport? Applied only for `backup` strategy, default - `4`
|
|
104
|
+
* @param {number} [opts.retries] - How many times resend failed emails
|
|
105
|
+
* @param {number} [opts.retryDelay] - Interval in milliseconds between send re-tries
|
|
106
|
+
* @param {boolean} [opts.keepHistory] - Keep queue task as it is in the database
|
|
107
|
+
* @param {boolean} [opts.concatEmails] - Concatenate email by `to` field, default - `false`
|
|
108
|
+
* @param {string} [opts.concatSubject] - Email subject used in concatenated email, default - `Multiple notifications`
|
|
109
|
+
* @param {string} [opts.concatDelimiter] - HTML or plain string delimiter used between concatenated email, default - `<hr>`
|
|
110
|
+
* @param {number} [opts.concatDelay] - HTML or plain string delimiter used between concatenated email, default - `<hr>`
|
|
111
|
+
* @param {number} [opts.revolvingInterval] - Interval in *milliseconds* in between queue checks, default - `256`
|
|
112
|
+
* @param {object|RedisAdapter|MongoAdapter|CustomAdapter} [opts.josk.adapter] - Interval in milliseconds between send re-tries
|
|
113
|
+
* @param {string} [opts.josk.adapter.type] - One of `mongo` or `redis`
|
|
114
|
+
* @param {string} [opts.prefix] - Optional prefix for scope isolation; use when creating multiple MailTime instances within the single application; By default prefix inherited from MailTime instance
|
|
115
|
+
*/
|
|
96
116
|
constructor (opts) {
|
|
97
117
|
if (!opts || typeof opts !== 'object' || opts === null) {
|
|
98
118
|
throw new TypeError('[mail-time] Configuration object must be passed into MailTime constructor');
|
|
99
119
|
}
|
|
100
120
|
|
|
101
|
-
if (!opts.
|
|
102
|
-
throw new Error('[mail-time]
|
|
121
|
+
if (!opts.queue || typeof opts.queue !== 'object') {
|
|
122
|
+
throw new Error('[mail-time] {queue} option is required', {
|
|
123
|
+
description: 'MailTime requires MongoQueue, RedisQueue, or CustomQueue to connect to an intermediate database'
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.queue = opts.queue;
|
|
128
|
+
this.queue.mailTimeInstance = this;
|
|
129
|
+
const queueMethods = ['ping', 'iterate', 'getPendingTo', 'push', 'remove', 'update', 'cancel'];
|
|
130
|
+
|
|
131
|
+
for (let i = queueMethods.length - 1; i >= 0; i--) {
|
|
132
|
+
if (typeof this.queue[queueMethods[i]] !== 'function') {
|
|
133
|
+
throw new Error(`{queue} instance is missing {${queueMethods[i]}} method that is required!`);
|
|
134
|
+
}
|
|
103
135
|
}
|
|
104
136
|
|
|
105
|
-
this.callbacks = {};
|
|
106
|
-
this.type = (!opts.type || (opts.type !== 'client' && opts.type !== 'server')) ? 'server' : opts.type;
|
|
107
137
|
this.debug = (opts.debug !== true) ? false : true;
|
|
108
|
-
this.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
this.template = (typeof opts.template === 'string') ? opts.template : '{{{html}}}';
|
|
112
|
-
this.zombieTime = opts.zombieTime || 32786;
|
|
113
|
-
this.keepHistory = opts.keepHistory || false;
|
|
138
|
+
this._debug = (...args) => {
|
|
139
|
+
debug(this.debug, ...args);
|
|
140
|
+
};
|
|
114
141
|
|
|
115
|
-
this.
|
|
116
|
-
this.
|
|
117
|
-
this.maxRevolvingDelay = opts.maxRevolvingDelay || 2048;
|
|
142
|
+
this.type = (typeof opts.type !== 'string' || (opts.type !== 'client' && opts.type !== 'server')) ? 'server' : opts.type;
|
|
143
|
+
this.prefix = (typeof opts.prefix === 'string') ? opts.prefix : '';
|
|
118
144
|
|
|
119
|
-
if (
|
|
120
|
-
this.
|
|
145
|
+
if (typeof opts.retries === 'number') {
|
|
146
|
+
this.maxTries = opts.retries + 1;
|
|
147
|
+
} else if (typeof opts.maxTries === 'number') {
|
|
148
|
+
this.maxTries = (opts.maxTries < 1) ? 1 : 0;
|
|
149
|
+
} else {
|
|
150
|
+
this.maxTries = 60;
|
|
121
151
|
}
|
|
122
152
|
|
|
123
|
-
if (
|
|
124
|
-
this.
|
|
153
|
+
if (typeof opts.retryDelay === 'number') {
|
|
154
|
+
this.retryDelay = opts.retryDelay;
|
|
155
|
+
} else if (typeof opts.interval === 'number') {
|
|
156
|
+
this.retryDelay = opts.interval * 1000;
|
|
157
|
+
} else {
|
|
158
|
+
this.retryDelay = 60000;
|
|
125
159
|
}
|
|
126
160
|
|
|
127
|
-
this.
|
|
161
|
+
this.template = (typeof opts.template === 'string') ? opts.template : '{{{html}}}';
|
|
162
|
+
this.keepHistory = (typeof opts.keepHistory === 'boolean') ? opts.keepHistory : false;
|
|
163
|
+
this.onSent = opts.onSent || noop;
|
|
164
|
+
this.onError = opts.onError || noop;
|
|
165
|
+
|
|
166
|
+
this.revolvingInterval = opts.revolvingInterval || 1536;
|
|
167
|
+
|
|
168
|
+
this.failsToNext = (typeof opts.failsToNext === 'number') ? opts.failsToNext : 4;
|
|
128
169
|
this.strategy = (opts.strategy === 'backup' || opts.strategy === 'balancer') ? opts.strategy : 'backup';
|
|
129
170
|
this.transports = opts.transports || [];
|
|
130
171
|
this.transport = 0;
|
|
@@ -145,64 +186,71 @@ module.exports = class MailTime {
|
|
|
145
186
|
this.concatEmails = (opts.concatEmails !== true) ? false : true;
|
|
146
187
|
this.concatSubject = (opts.concatSubject && typeof opts.concatSubject === 'string') ? opts.concatSubject : 'Multiple notifications';
|
|
147
188
|
this.concatDelimiter = (opts.concatDelimiter && typeof opts.concatDelimiter === 'string') ? opts.concatDelimiter : '<hr>';
|
|
148
|
-
this.concatThrottling = ((opts.concatThrottling && !isNaN(opts.concatThrottling)) ? parseInt(opts.concatThrottling) : 60) * 1000;
|
|
149
189
|
|
|
150
|
-
if (
|
|
151
|
-
this.
|
|
190
|
+
if (typeof opts.concatDelay === 'number') {
|
|
191
|
+
this.concatDelay = opts.concatDelay;
|
|
192
|
+
} else if (typeof opts.concatThrottling === 'number') {
|
|
193
|
+
this.concatDelay = opts.concatThrottling * 1000;
|
|
194
|
+
} else {
|
|
195
|
+
this.concatDelay = 60000;
|
|
152
196
|
}
|
|
153
197
|
|
|
154
|
-
_debug(
|
|
155
|
-
_debug(
|
|
156
|
-
_debug(
|
|
157
|
-
_debug(
|
|
158
|
-
_debug(
|
|
159
|
-
_debug(
|
|
198
|
+
this._debug('DEBUG ON {debug: true}');
|
|
199
|
+
this._debug(`INITIALIZING [type: ${this.type}]`);
|
|
200
|
+
this._debug(`INITIALIZING [strategy: ${this.strategy}]`);
|
|
201
|
+
this._debug(`INITIALIZING [josk.adapter: ${opts?.josk?.adapter}]`);
|
|
202
|
+
this._debug(`INITIALIZING [prefix: ${this.prefix}]`);
|
|
203
|
+
this._debug(`INITIALIZING [retries: ${this.retries}]`);
|
|
204
|
+
this._debug(`INITIALIZING [failsToNext: ${this.failsToNext}]`);
|
|
205
|
+
this._debug(`INITIALIZING [onError: ${this.onError}]`);
|
|
206
|
+
this._debug(`INITIALIZING [onSent: ${this.onSent}]`);
|
|
207
|
+
|
|
208
|
+
/** SERVER-SPECIFIC CHECKS AND CONFIG */
|
|
209
|
+
if (this.type === 'server') {
|
|
210
|
+
if (this.transports.constructor !== Array || !this.transports.length) {
|
|
211
|
+
throw new Error('[mail-time] {transports} is required for {type: "server"} and must be an Array, like returned from `nodemailer.createTransport`');
|
|
212
|
+
}
|
|
160
213
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
214
|
+
if (typeof opts.josk !== 'object') {
|
|
215
|
+
throw new Error('[mail-time] {josk} option is required {object} for {type: "server"}');
|
|
216
|
+
}
|
|
164
217
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (indexError) {
|
|
168
|
-
_logError('[createIndex]', indexError);
|
|
218
|
+
if (typeof opts.josk.adapter !== 'object') {
|
|
219
|
+
throw new Error('[mail-time] {josk.adapter} option is required {object} *or* custom adapter Class');
|
|
169
220
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
221
|
+
|
|
222
|
+
this.josk = { ...opts.josk };
|
|
223
|
+
|
|
224
|
+
if (typeof opts.josk.adapter?.type === 'string' && opts.josk.adapter?.type === 'mongo') {
|
|
225
|
+
if (!opts.josk.adapter.db) {
|
|
226
|
+
throw new Error('[mail-time] {josk.adapter.db} option required for {josk.adapter.type: "mongo"}');
|
|
227
|
+
}
|
|
228
|
+
this.josk.adapter = new MongoAdapter({ prefix: `mailTimeQueue${this.prefix}`, ...opts.josk.adapter });
|
|
174
229
|
}
|
|
175
|
-
});
|
|
176
|
-
// Schema:
|
|
177
|
-
// _id
|
|
178
|
-
// to {String|[String]}
|
|
179
|
-
// tries {Number} - qty of send attempts
|
|
180
|
-
// sendAt {Date} - When letter should be sent
|
|
181
|
-
// isSent {Boolean} - Email status
|
|
182
|
-
// template {String} - Template for this email
|
|
183
|
-
// transport {Number} - Last used transport
|
|
184
|
-
// concatSubject {String|Boolean} - Email concatenation subject
|
|
185
|
-
// ---
|
|
186
|
-
// mailOptions {[Object]} - Array of nodeMailer's `mailOptions`
|
|
187
|
-
// mailOptions.to {String|Array} - [REQUIRED]
|
|
188
|
-
// mailOptions.from {String}
|
|
189
|
-
// mailOptions.text {String|Boolean}
|
|
190
|
-
// mailOptions.html (String)
|
|
191
|
-
// mailOptions.subject {String}
|
|
192
|
-
// mailOptions.Other nodeMailer `sendMail` options...
|
|
193
230
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
231
|
+
if (typeof opts.josk.adapter?.type === 'string' && opts.josk.adapter?.type === 'redis') {
|
|
232
|
+
if (!opts.josk.adapter.client) {
|
|
233
|
+
throw new Error('[mail-time] {josk.adapter.client} option required for {josk.adapter.type: "redis"}');
|
|
234
|
+
}
|
|
235
|
+
this.josk.adapter = new RedisAdapter({ prefix: `mailTimeQueue${this.prefix}`, ...opts.josk.adapter });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this.josk.minRevolvingDelay = (typeof opts.josk.minRevolvingDelay === 'number') ? opts.josk.minRevolvingDelay : 512;
|
|
239
|
+
this.josk.maxRevolvingDelay = (typeof opts.josk.maxRevolvingDelay === 'number') ? opts.josk.maxRevolvingDelay : 2048;
|
|
240
|
+
this.josk.zombieTime = (typeof opts.josk.zombieTime === 'number') ? opts.josk.zombieTime : 32786;
|
|
241
|
+
|
|
242
|
+
this.scheduler = new JoSk({
|
|
197
243
|
debug: this.debug,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
244
|
+
...this.josk,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
process.nextTick(async () => {
|
|
248
|
+
if ((await this.scheduler.ping()).status !== 'OK') {
|
|
249
|
+
throw new Error('[mail-time] [MailTime#ping] can not connect to storage, make sure it is available and properly configured');
|
|
250
|
+
}
|
|
203
251
|
});
|
|
204
252
|
|
|
205
|
-
scheduler.setInterval(this.
|
|
253
|
+
this.scheduler.setInterval(this.___iterate.bind(this), this.revolvingInterval, `mailTimeQueue${this.prefix}`);
|
|
206
254
|
}
|
|
207
255
|
}
|
|
208
256
|
|
|
@@ -214,372 +262,234 @@ module.exports = class MailTime {
|
|
|
214
262
|
DEFAULT_TEMPLATE = newVal;
|
|
215
263
|
}
|
|
216
264
|
|
|
217
|
-
___compileMailOpts(transport, task) {
|
|
218
|
-
let _mailOpts = {};
|
|
219
|
-
|
|
220
|
-
if (!transport) {
|
|
221
|
-
throw new Error('[mail-time] [sendMail] [___compileMailOpts] transport not available or misconfiguration is in place!');
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (transport._options && typeof transport._options === 'object' && transport._options !== null && transport._options.mailOptions) {
|
|
225
|
-
_mailOpts = merge(_mailOpts, transport._options.mailOptions);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (transport.options && typeof transport.options === 'object' && transport.options !== null && transport.options.mailOptions) {
|
|
229
|
-
_mailOpts = merge(_mailOpts, transport.options.mailOptions);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
_mailOpts = merge(_mailOpts, {
|
|
233
|
-
html: '',
|
|
234
|
-
text: '',
|
|
235
|
-
subject: ''
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
for (let i = 0; i < task.mailOptions.length; i++) {
|
|
239
|
-
if (task.mailOptions[i].html) {
|
|
240
|
-
if (task.mailOptions.length > 1) {
|
|
241
|
-
_mailOpts.html += this.___render(this.concatDelimiter, task.mailOptions[i]) + this.___render(task.mailOptions[i].html, task.mailOptions[i]);
|
|
242
|
-
} else {
|
|
243
|
-
_mailOpts.html = this.___render(task.mailOptions[i].html, task.mailOptions[i]);
|
|
244
|
-
}
|
|
245
|
-
delete task.mailOptions[i].html;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (task.mailOptions[i].text) {
|
|
249
|
-
if (task.mailOptions.length > 1) {
|
|
250
|
-
_mailOpts.text += '\r\n' + this.___render(task.mailOptions[i].text, task.mailOptions[i]);
|
|
251
|
-
} else {
|
|
252
|
-
_mailOpts.text = this.___render(task.mailOptions[i].text, task.mailOptions[i]);
|
|
253
|
-
}
|
|
254
|
-
delete task.mailOptions[i].text;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
_mailOpts = merge(_mailOpts, task.mailOptions[i]);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (_mailOpts.html && (task.template || this.template)) {
|
|
261
|
-
_mailOpts.html = this.___render((task.template || this.template), _mailOpts);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (task.mailOptions.length > 1) {
|
|
265
|
-
_mailOpts.subject = task.concatSubject || this.concatSubject || _mailOpts.subject;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (!_mailOpts.from && this.from) {
|
|
269
|
-
_mailOpts.from = this.from(transport);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
return _mailOpts;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
265
|
/**
|
|
266
|
+
* @async
|
|
276
267
|
* @memberOf MailTime
|
|
277
|
-
* @name
|
|
278
|
-
* @
|
|
279
|
-
* @returns {
|
|
268
|
+
* @name ping
|
|
269
|
+
* @description Check package readiness and connection to Storage
|
|
270
|
+
* @returns {Promise<object>}
|
|
271
|
+
* @throws {mix}
|
|
280
272
|
*/
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
$lt: this.maxTries
|
|
289
|
-
}
|
|
290
|
-
}, {
|
|
291
|
-
projection: {
|
|
292
|
-
_id: 1,
|
|
293
|
-
tries: 1,
|
|
294
|
-
template: 1,
|
|
295
|
-
transport: 1,
|
|
296
|
-
mailOptions: 1,
|
|
297
|
-
concatSubject: 1
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
cursor.forEach((task) => {
|
|
302
|
-
this.collection.updateOne({
|
|
303
|
-
_id: task._id
|
|
304
|
-
}, {
|
|
305
|
-
$set: {
|
|
306
|
-
isSent: true
|
|
307
|
-
},
|
|
308
|
-
$inc: {
|
|
309
|
-
tries: 1
|
|
310
|
-
}
|
|
311
|
-
}, (updateError) => {
|
|
312
|
-
if (updateError) {
|
|
313
|
-
this.___handleError(task, updateError, {});
|
|
314
|
-
} else {
|
|
315
|
-
let transport;
|
|
316
|
-
let transportIndex;
|
|
317
|
-
if (this.strategy === 'balancer') {
|
|
318
|
-
this.transport = this.transport + 1;
|
|
319
|
-
if (this.transport >= this.transports.length) {
|
|
320
|
-
this.transport = 0;
|
|
321
|
-
}
|
|
322
|
-
transportIndex = this.transport;
|
|
323
|
-
transport = this.transports[transportIndex];
|
|
324
|
-
} else {
|
|
325
|
-
transportIndex = task.transport;
|
|
326
|
-
transport = this.transports[transportIndex];
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
try {
|
|
330
|
-
const _mailOpts = this.___compileMailOpts(transport, task);
|
|
331
|
-
|
|
332
|
-
_debug(this.debug, '[sendMail] [sending] To:', _mailOpts.to);
|
|
333
|
-
transport.sendMail(_mailOpts, (error, info) => {
|
|
334
|
-
if (error) {
|
|
335
|
-
this.___handleError(task, error, info);
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (info.accepted && !info.accepted.length) {
|
|
340
|
-
this.___handleError(task, 'Message not accepted or Greeting never received', info);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
_debug(this.debug, `email successfully sent, attempts: #${task.tries}, transport #${transportIndex} to: `, _mailOpts.to);
|
|
345
|
-
if (this.keepHistory) {
|
|
346
|
-
this.___triggerCallbacks(void 0, task, info);
|
|
347
|
-
} else {
|
|
348
|
-
this.collection.deleteOne({ _id: task._id }, () => {
|
|
349
|
-
this.___triggerCallbacks(void 0, task, info);
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
return;
|
|
354
|
-
});
|
|
355
|
-
} catch (e) {
|
|
356
|
-
_logError('Exception during runtime:', e);
|
|
357
|
-
this.___handleError(task, e, {});
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
}, (forEachError) => {
|
|
362
|
-
ready();
|
|
363
|
-
cursor.close();
|
|
364
|
-
if (forEachError) {
|
|
365
|
-
_logError('[___send] [forEach] [forEachError]', forEachError);
|
|
366
|
-
}
|
|
367
|
-
});
|
|
273
|
+
async ping() {
|
|
274
|
+
this._debug('[ping]');
|
|
275
|
+
const schedulerPing = await this.scheduler.ping();
|
|
276
|
+
if (schedulerPing.status !== 'OK') {
|
|
277
|
+
return schedulerPing;
|
|
278
|
+
}
|
|
279
|
+
return await this.queue.ping();
|
|
368
280
|
}
|
|
369
281
|
|
|
370
282
|
/**
|
|
371
283
|
* @memberOf MailTime
|
|
372
284
|
* @name send
|
|
373
285
|
* @description alias of `sendMail`
|
|
374
|
-
* @returns {
|
|
286
|
+
* @returns {Promise<string>} uuid of the email
|
|
375
287
|
*/
|
|
376
|
-
send(opts
|
|
377
|
-
this.
|
|
288
|
+
async send(opts) {
|
|
289
|
+
this._debug('[send]', opts);
|
|
290
|
+
return await this.sendMail(opts);
|
|
378
291
|
}
|
|
379
292
|
|
|
380
293
|
/**
|
|
294
|
+
* @async
|
|
381
295
|
* @memberOf MailTime
|
|
382
296
|
* @name sendMail
|
|
383
|
-
* @
|
|
384
|
-
* @param opts
|
|
385
|
-
* @param opts.
|
|
386
|
-
* @param opts
|
|
387
|
-
* @param
|
|
388
|
-
* @returns {
|
|
297
|
+
* @description add email to the queue or append to existing letter if {concatEmails: true}
|
|
298
|
+
* @param opts {object} - email options
|
|
299
|
+
* @param opts.sendAt {Date|number} - When email should be sent
|
|
300
|
+
* @param opts.template {string} - Email-specific template
|
|
301
|
+
* @param opts[key] {mix} - Other NodeMailer's options
|
|
302
|
+
* @returns {Promise<string>} uuid of the email
|
|
303
|
+
* @throws {Error}
|
|
389
304
|
*/
|
|
390
|
-
sendMail(opts = {}
|
|
391
|
-
_debug(
|
|
305
|
+
async sendMail(opts = {}) {
|
|
306
|
+
this._debug('[sendMail]', opts);
|
|
392
307
|
if (!opts.html && !opts.text) {
|
|
393
308
|
throw new Error('`html` nor `text` field is presented, at least one of those fields is required');
|
|
394
309
|
}
|
|
395
310
|
|
|
396
|
-
|
|
397
|
-
|
|
311
|
+
let sendAt = opts.sendAt;
|
|
312
|
+
if (!sendAt) {
|
|
313
|
+
sendAt = Date.now();
|
|
398
314
|
}
|
|
399
315
|
|
|
400
|
-
if (
|
|
401
|
-
|
|
316
|
+
if (sendAt instanceof Date) {
|
|
317
|
+
sendAt = +sendAt;
|
|
402
318
|
}
|
|
403
319
|
|
|
404
|
-
if (typeof
|
|
405
|
-
|
|
320
|
+
if (typeof sendAt !== 'number') {
|
|
321
|
+
sendAt = Date.now();
|
|
406
322
|
}
|
|
407
323
|
|
|
408
|
-
let
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
324
|
+
let template = opts.template;
|
|
325
|
+
if (typeof template !== 'string') {
|
|
326
|
+
template = false;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
let concatSubject = opts.concatSubject;
|
|
330
|
+
if (typeof concatSubject !== 'string') {
|
|
331
|
+
concatSubject = false;
|
|
332
|
+
}
|
|
414
333
|
|
|
415
|
-
|
|
334
|
+
const mailOptions = { ...opts };
|
|
335
|
+
delete mailOptions.sendAt;
|
|
336
|
+
delete mailOptions.template;
|
|
337
|
+
delete mailOptions.concatSubject;
|
|
338
|
+
|
|
339
|
+
if (typeof mailOptions.to !== 'string' && (!(mailOptions.to instanceof Array) || !mailOptions.to.length)) {
|
|
416
340
|
throw new Error('[mail-time] `mailOptions.to` is required and must be a string or non-empty Array');
|
|
417
341
|
}
|
|
418
342
|
|
|
419
343
|
if (this.concatEmails) {
|
|
420
|
-
this.
|
|
421
|
-
|
|
422
|
-
isSent: false,
|
|
423
|
-
sendAt: {
|
|
424
|
-
$lte: new Date(+_sendAt + this.concatThrottling)
|
|
425
|
-
}
|
|
426
|
-
}, {
|
|
427
|
-
projection: {
|
|
428
|
-
_id: 1,
|
|
429
|
-
mailOptions: 1
|
|
430
|
-
}
|
|
431
|
-
}, (findError, task) => {
|
|
432
|
-
if (findError) {
|
|
433
|
-
_debug(this.debug, 'something went wrong, can\'t send email to: ', opts.to, findError);
|
|
434
|
-
callback(findError, void 0, task);
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
344
|
+
sendAt = sendAt + this.concatDelay;
|
|
345
|
+
const task = await this.queue.getPendingTo(mailOptions.to, sendAt);
|
|
437
346
|
|
|
438
|
-
|
|
439
|
-
|
|
347
|
+
if (task) {
|
|
348
|
+
const pendingMailOptions = task.mailOptions || [];
|
|
440
349
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
350
|
+
for (let i = 0; i < pendingMailOptions.length; i++) {
|
|
351
|
+
if (equals(pendingMailOptions[i], mailOptions)) {
|
|
352
|
+
return task.uuid;
|
|
445
353
|
}
|
|
446
|
-
|
|
447
|
-
queue.push(opts);
|
|
448
|
-
|
|
449
|
-
this.collection.updateOne({
|
|
450
|
-
_id: task._id
|
|
451
|
-
}, {
|
|
452
|
-
$set: {
|
|
453
|
-
mailOptions: queue
|
|
454
|
-
}
|
|
455
|
-
}, (updateError) => {
|
|
456
|
-
if (updateError) {
|
|
457
|
-
_debug('something went wrong, can\'t send email to: ', task.mailOptions[0].to, updateError);
|
|
458
|
-
callback(updateError, void 0, task);
|
|
459
|
-
}
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
return;
|
|
463
354
|
}
|
|
464
355
|
|
|
465
|
-
|
|
466
|
-
this.
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}, callback);
|
|
472
|
-
return;
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
return;
|
|
356
|
+
pendingMailOptions.push(opts);
|
|
357
|
+
await this.queue.update(task, {
|
|
358
|
+
mailOptions: pendingMailOptions
|
|
359
|
+
});
|
|
360
|
+
return task.uuid;
|
|
361
|
+
}
|
|
476
362
|
}
|
|
477
363
|
|
|
478
|
-
this.___addToQueue({
|
|
479
|
-
sendAt
|
|
480
|
-
template
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
364
|
+
return await this.___addToQueue({
|
|
365
|
+
sendAt,
|
|
366
|
+
template,
|
|
367
|
+
concatSubject,
|
|
368
|
+
mailOptions: mailOptions,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
484
371
|
|
|
485
|
-
|
|
372
|
+
/**
|
|
373
|
+
* @memberOf MailTime
|
|
374
|
+
* @name cancel
|
|
375
|
+
* @description alias of `cancelMail`
|
|
376
|
+
* @returns {Promise<boolean>} returns `true` if cancelled or `false` if not found was sent or was cancelled previously
|
|
377
|
+
*/
|
|
378
|
+
async cancel(uuid) {
|
|
379
|
+
this._debug('[cancel]', uuid);
|
|
380
|
+
return await this.cancelMail(uuid);
|
|
486
381
|
}
|
|
487
382
|
|
|
488
383
|
/**
|
|
384
|
+
* @async
|
|
385
|
+
* @memberOf MailTime
|
|
386
|
+
* @name cancelMail
|
|
387
|
+
* @description remove email from the queue or mark as `isCancelled`
|
|
388
|
+
* @param uuid {string|Promise<string>} - uuid returned from `send` or `sendMail`
|
|
389
|
+
* @returns {Promise<boolean>} returns `true` if cancelled or `false` if not found was sent or was cancelled previously
|
|
390
|
+
*/
|
|
391
|
+
async cancelMail(uuid) {
|
|
392
|
+
this._debug('[cancelMail]', uuid);
|
|
393
|
+
if (typeof uuid === 'object' && uuid instanceof Promise) {
|
|
394
|
+
return await this.queue.cancel(await uuid);
|
|
395
|
+
}
|
|
396
|
+
return await this.queue.cancel(uuid);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* @async
|
|
489
401
|
* @memberOf MailTime
|
|
490
402
|
* @name ___handleError
|
|
491
|
-
* @
|
|
492
|
-
* @param
|
|
493
|
-
* @param
|
|
494
|
-
* @
|
|
403
|
+
* @description Handle runtime errors and pass to `onError` callback
|
|
404
|
+
* @param task {object} - Email task record form Storage
|
|
405
|
+
* @param error {mix} - Error String/Object/Error
|
|
406
|
+
* @param info {object} - Info object returned from nodemailer
|
|
407
|
+
* @returns {Promise<void 0>}
|
|
495
408
|
*/
|
|
496
|
-
___handleError(task, error, info) {
|
|
409
|
+
async ___handleError(task, error, info) {
|
|
410
|
+
this._debug('[private handleError]', { task, error, info });
|
|
497
411
|
if (!task) {
|
|
498
412
|
return;
|
|
499
413
|
}
|
|
500
414
|
|
|
501
415
|
if (task.tries >= this.maxTries) {
|
|
416
|
+
task.isSent = false;
|
|
417
|
+
task.isFailed = true;
|
|
418
|
+
|
|
502
419
|
if (!this.keepHistory) {
|
|
503
|
-
this.
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
420
|
+
await this.queue.remove(task);
|
|
421
|
+
} else {
|
|
422
|
+
await this.queue.update(task, {
|
|
423
|
+
isSent: task.isSent,
|
|
424
|
+
isFailed: task.isFailed,
|
|
508
425
|
});
|
|
509
426
|
}
|
|
510
|
-
} else {
|
|
511
|
-
let transportIndex = task.transport;
|
|
512
427
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
}
|
|
518
|
-
}
|
|
428
|
+
this._debug(`[private handleError] Giving up trying send email after ${task.tries} attempts to: `, task.mailOptions[0].to, error);
|
|
429
|
+
this.onError(error, task, info);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
519
432
|
|
|
520
|
-
|
|
521
|
-
_id: task._id
|
|
522
|
-
}, {
|
|
523
|
-
$set: {
|
|
524
|
-
isSent: false,
|
|
525
|
-
sendAt: new Date(Date.now() + this.interval),
|
|
526
|
-
transport: transportIndex
|
|
527
|
-
}
|
|
528
|
-
}, mongoErrorHandler);
|
|
433
|
+
let transportIndex = task.transport;
|
|
529
434
|
|
|
530
|
-
|
|
435
|
+
if (this.strategy === 'backup' && this.transports.length > 1 && (task.tries % this.failsToNext) === 0) {
|
|
436
|
+
++transportIndex;
|
|
437
|
+
if (transportIndex > this.transports.length - 1) {
|
|
438
|
+
transportIndex = 0;
|
|
439
|
+
}
|
|
531
440
|
}
|
|
441
|
+
|
|
442
|
+
await this.queue.update(task, {
|
|
443
|
+
isSent: false,
|
|
444
|
+
sendAt: Date.now() + this.retryDelay,
|
|
445
|
+
transport: transportIndex,
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
this._debug(`[private handleError] Next re-send attempt at ${new Date(Date.now() + this.retryDelay)}: #${task.tries}/${this.maxTries}, transport #${transportIndex} to: `, task.mailOptions[0].to, error);
|
|
532
449
|
}
|
|
533
450
|
|
|
534
451
|
/**
|
|
452
|
+
* @async
|
|
535
453
|
* @memberOf MailTime
|
|
536
454
|
* @name ___addToQueue
|
|
537
|
-
* @
|
|
538
|
-
* @param opts
|
|
539
|
-
* @param opts.
|
|
540
|
-
* @param opts.
|
|
541
|
-
* @param opts.
|
|
542
|
-
* @param
|
|
543
|
-
* @returns {
|
|
455
|
+
* @description Prepare task's object and push to the queue
|
|
456
|
+
* @param opts {object} - Email options with next properties:
|
|
457
|
+
* @param opts.sendAt {number} - When email should be sent
|
|
458
|
+
* @param opts.template {string} - Email-specific template
|
|
459
|
+
* @param opts.mailOptions {object} - MailOptions according to NodeMailer lib
|
|
460
|
+
* @param opts.concatSubject {string} - Email subject used when sending concatenated email
|
|
461
|
+
* @returns {Promise<string>} message uuid
|
|
544
462
|
*/
|
|
545
|
-
___addToQueue(opts
|
|
546
|
-
_debug(
|
|
463
|
+
async ___addToQueue(opts) {
|
|
464
|
+
this._debug('[private addToQueue]', opts);
|
|
547
465
|
const task = {
|
|
466
|
+
uuid: crypto.randomUUID(),
|
|
548
467
|
tries: 0,
|
|
549
468
|
isSent: false,
|
|
550
469
|
sendAt: opts.sendAt,
|
|
470
|
+
isFailed: false,
|
|
551
471
|
template: opts.template,
|
|
552
472
|
transport: this.transport,
|
|
473
|
+
isCancelled: false,
|
|
553
474
|
mailOptions: [opts.mailOptions],
|
|
554
|
-
concatSubject: opts.concatSubject
|
|
475
|
+
concatSubject: opts.concatSubject,
|
|
555
476
|
};
|
|
556
477
|
|
|
557
478
|
if (this.concatEmails) {
|
|
558
479
|
task.to = opts.mailOptions.to;
|
|
559
480
|
}
|
|
560
481
|
|
|
561
|
-
this.
|
|
562
|
-
|
|
563
|
-
_logError('something went wrong, can\'t send email to: ', opts.mailOptions[0].to, insertError);
|
|
564
|
-
callback(insertError, void 0, opts);
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
const _id = r.insertedId.toHexString();
|
|
569
|
-
if (!this.callbacks[_id]) {
|
|
570
|
-
this.callbacks[_id] = [];
|
|
571
|
-
}
|
|
572
|
-
this.callbacks[_id].push(callback);
|
|
573
|
-
return;
|
|
574
|
-
});
|
|
482
|
+
await this.queue.push(task);
|
|
483
|
+
return task.uuid;
|
|
575
484
|
}
|
|
576
485
|
|
|
577
486
|
/**
|
|
578
487
|
* @memberOf MailTime
|
|
579
488
|
* @name ___render
|
|
580
|
-
* @
|
|
581
|
-
* @param
|
|
582
|
-
* @
|
|
489
|
+
* @description Render templates
|
|
490
|
+
* @param string {string} - Template with Mustache-like placeholders
|
|
491
|
+
* @param replacements {object} - Blaze/Mustache-like helpers Object
|
|
492
|
+
* @returns {string}
|
|
583
493
|
*/
|
|
584
494
|
___render(_string, replacements) {
|
|
585
495
|
let i;
|
|
@@ -606,19 +516,154 @@ module.exports = class MailTime {
|
|
|
606
516
|
|
|
607
517
|
/**
|
|
608
518
|
* @memberOf MailTime
|
|
609
|
-
* @name
|
|
610
|
-
* @
|
|
611
|
-
* @param
|
|
612
|
-
* @param info
|
|
519
|
+
* @name ___compileMailOpts
|
|
520
|
+
* @description Run various checks, compile options, and render template
|
|
521
|
+
* @param transport {object} - Current transport
|
|
522
|
+
* @param info {object} - Info object returned from NodeMailer's `sendMail` method
|
|
613
523
|
* @returns {void 0}
|
|
614
524
|
*/
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
525
|
+
___compileMailOpts(transport, task) {
|
|
526
|
+
let compiledOpts = {};
|
|
527
|
+
|
|
528
|
+
if (!transport) {
|
|
529
|
+
throw new Error('[mail-time] [sendMail] [___compileMailOpts] {transport} is not available or misconfigured!');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (transport._options && typeof transport._options === 'object' && transport._options !== null && transport._options.mailOptions) {
|
|
533
|
+
compiledOpts = merge(compiledOpts, transport._options.mailOptions);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (transport.options && typeof transport.options === 'object' && transport.options !== null && transport.options.mailOptions) {
|
|
537
|
+
compiledOpts = merge(compiledOpts, transport.options.mailOptions);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
compiledOpts = merge(compiledOpts, {
|
|
541
|
+
html: '',
|
|
542
|
+
text: '',
|
|
543
|
+
subject: ''
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
for (let i = 0; i < task.mailOptions.length; i++) {
|
|
547
|
+
if (task.mailOptions[i].html) {
|
|
548
|
+
if (task.mailOptions.length > 1) {
|
|
549
|
+
compiledOpts.html += this.___render(this.concatDelimiter, task.mailOptions[i]) + this.___render(task.mailOptions[i].html, task.mailOptions[i]);
|
|
550
|
+
} else {
|
|
551
|
+
compiledOpts.html = this.___render(task.mailOptions[i].html, task.mailOptions[i]);
|
|
552
|
+
}
|
|
553
|
+
delete task.mailOptions[i].html;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (task.mailOptions[i].text) {
|
|
557
|
+
if (task.mailOptions.length > 1) {
|
|
558
|
+
compiledOpts.text += '\r\n' + this.___render(task.mailOptions[i].text, task.mailOptions[i]);
|
|
559
|
+
} else {
|
|
560
|
+
compiledOpts.text = this.___render(task.mailOptions[i].text, task.mailOptions[i]);
|
|
561
|
+
}
|
|
562
|
+
delete task.mailOptions[i].text;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
compiledOpts = merge(compiledOpts, task.mailOptions[i]);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (compiledOpts.html && (task.template || this.template)) {
|
|
569
|
+
compiledOpts.html = this.___render((task.template || this.template), compiledOpts);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (task.mailOptions.length > 1) {
|
|
573
|
+
compiledOpts.subject = task.concatSubject || this.concatSubject || compiledOpts.subject;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (!compiledOpts.from && this.from) {
|
|
577
|
+
compiledOpts.from = this.from(transport);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
return compiledOpts;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* @async
|
|
585
|
+
* @memberOf MailTime
|
|
586
|
+
* @name ___send
|
|
587
|
+
* @description send email using nodemailer's transport
|
|
588
|
+
* @param task {object} - email's task object from Storage
|
|
589
|
+
* @returns {Promise<void 0>}
|
|
590
|
+
*/
|
|
591
|
+
async ___send(task) {
|
|
592
|
+
this._debug('[private send]', task);
|
|
593
|
+
try {
|
|
594
|
+
if (task.isSent === true || task.isFailed === true || task.isCancelled === true) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
task.tries++;
|
|
599
|
+
const isUpdated = await this.queue.update(task, {
|
|
600
|
+
isSent: true,
|
|
601
|
+
tries: task.tries
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
if (!isUpdated) {
|
|
605
|
+
logError('[private send] [queue.update] Not updated!');
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
let transport;
|
|
610
|
+
let transportIndex;
|
|
611
|
+
if (this.strategy === 'balancer') {
|
|
612
|
+
this.transport = this.transport + 1;
|
|
613
|
+
if (this.transport >= this.transports.length) {
|
|
614
|
+
this.transport = 0;
|
|
615
|
+
}
|
|
616
|
+
transportIndex = this.transport;
|
|
617
|
+
transport = this.transports[transportIndex];
|
|
618
|
+
} else {
|
|
619
|
+
transportIndex = task.transport;
|
|
620
|
+
transport = this.transports[transportIndex];
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const compiledOpts = this.___compileMailOpts(transport, task);
|
|
624
|
+
|
|
625
|
+
await new Promise((resolve) => {
|
|
626
|
+
transport.sendMail(compiledOpts, async (error, info) => {
|
|
627
|
+
this._debug('[private send] [sending]', { error, info });
|
|
628
|
+
if (error) {
|
|
629
|
+
await this.___handleError(task, error, info);
|
|
630
|
+
resolve();
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if (info.accepted && !info.accepted.length) {
|
|
635
|
+
await this.___handleError(task, new Error('Message not accepted or Greeting never received'), info);
|
|
636
|
+
resolve();
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
this._debug(`email successfully sent, attempts: #${task.tries}, transport #${transportIndex} to: `, compiledOpts.to);
|
|
641
|
+
|
|
642
|
+
if (!this.keepHistory) {
|
|
643
|
+
await this.queue.remove(task);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
task.isSent = true;
|
|
647
|
+
this.onSent(task, info);
|
|
648
|
+
resolve();
|
|
649
|
+
});
|
|
620
650
|
});
|
|
651
|
+
} catch (e) {
|
|
652
|
+
logError('Exception during runtime:', e);
|
|
653
|
+
this.___handleError(task, e, {});
|
|
621
654
|
}
|
|
622
|
-
delete this.callbacks[_id];
|
|
623
655
|
}
|
|
624
|
-
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* @memberOf MailTime
|
|
659
|
+
* @name ___iterate
|
|
660
|
+
* @description Iterate over queued tasks
|
|
661
|
+
* @returns {Promise}
|
|
662
|
+
*/
|
|
663
|
+
async ___iterate() {
|
|
664
|
+
this._debug('[private iterate]');
|
|
665
|
+
return await this.queue.iterate();
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
export { MailTime, MongoQueue, RedisQueue };
|