mail-time 1.2.6 → 1.3.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 CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2021, dr.dimitru (Dmitry A.; Veliov Group, LLC)
1
+ Copyright (c) 2022, dr.dimitru (Dmitry A.; Veliov Group, LLC)
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms,
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![support](https://img.shields.io/badge/support-GitHub-white)](https://github.com/sponsors/dr-dimitru)
1
+ [![support](https://img.shields.io/badge/support-GitHub-white)](https://github.com/sponsors/veliovgroup)
2
2
  [![support](https://img.shields.io/badge/support-PayPal-white)](https://paypal.me/veliovgroup)
3
3
  <a href="https://ostr.io/info/built-by-developers-for-developers">
4
4
  <img src="https://ostr.io/apple-touch-icon-60x60.png" height="20">
@@ -14,6 +14,12 @@ Every `MailTime` instance can be configured to be a *Server* or *Client*.
14
14
  Main difference of *Server* from *Client* - *Server* handles queue and actually sends email.
15
15
  While *Client* is only put emails into the queue.
16
16
 
17
+ "Mail-Time" is a micro-service package for mail queue, with *Server* and *Client* APIs. Build on top of the [`nodemailer`](https://github.com/nodemailer/nodemailer) package.
18
+
19
+ Every `MailTime` instance can get configured as *Server* or *Client*.
20
+
21
+ The main difference between *Server* and *Client* modes is that the *Server* handles the queue and __sends__ email. While the *Client* only __adds__ emails into the queue.
22
+
17
23
  ## ToC
18
24
 
19
25
  - [How it works?](https://github.com/veliovgroup/Mail-Time#how-it-works)
@@ -45,7 +51,7 @@ Redundant solution for email transmission.
45
51
 
46
52
  ### Single point of failure
47
53
 
48
- Issue - classic solution with the single point of failure:
54
+ Issue - classic solution with a single point of failure:
49
55
 
50
56
  ```ascii
51
57
  |----------------| |------| |------------------|
@@ -93,11 +99,11 @@ Backup scheme with multiple SMTP providers
93
99
 
94
100
  ### Cluster issue
95
101
 
96
- Let's say you have an app which is growing fast. At some point, you've decided to create a "Cluster" of servers to balance the load and add durability layer.
102
+ It is common to create a "Cluster" of servers to balance the load and add a durability layer for horizontal scaling of quickly growing applications.
97
103
 
98
- Also, your application has scheduled emails, for example, once a day with recent news. While you have had single server emails was sent by some daily interval. So, after you made a "Cluster" of servers - each server has its own timer and going to send a daily email to our user. In such case - users will receive 3 emails, sounds not okay.
104
+ Most modern application has scheduled or recurring emails. For example, once a day — with recent news and updates. It won't be an issue with a single server setup — the server would send emails at a daily interval via timer or CRON. While in "Cluster" implementation — each server will attempt to send the same email. In such cases, users will receive multiple emails with the same content. We built MailTime to address this and other similar issues.
99
105
 
100
- Here is how we solve this issue:
106
+ Here is how this issue is solved by using MailTime:
101
107
 
102
108
  ```ascii
103
109
  |===================THE=CLUSTER===================| |=QUEUE=| |===Mail=Time===|
@@ -117,17 +123,17 @@ Here is how we solve this issue:
117
123
 
118
124
  ## Features
119
125
 
120
- - Queue - Managed via MongoDB, and will survive server reboots and failures
126
+ - Queue - Managed via MongoDB, will survive server reboots and failures
121
127
  - Support for multiple server setups - "Cluster", Phusion Passenger instances, Load Balanced solutions, etc.
122
128
  - Emails concatenation by addressee email - Reduce amount of sent emails to a single user with concatenation, and avoid mistakenly doubled emails
123
129
  - When concatenation is enabled - Same emails won't be sent twice, if for any reason, due to bad logic or application failure emails are sent twice or more times - this is solution to solve this annoying behavior
124
- - Balancing for multiple nodemailer's transports, two modes - `backup` and `balancing`. Most useful feature - allows to reduce the cost of SMTP services and add durability. So, if any of used transports are failing to send an email it will switch to next one
130
+ - Balancing for multiple nodemailer's transports, two modes - `backup` and `balancing`. This is the most useful feature — allowing to reduce the cost of SMTP services and add extra layer of durability. If one transport failing to send an email `mail-time` will switch to the next one
125
131
  - Sending retries for network and other failures
126
- - Template support with Mustache-like placeholders
132
+ - Templating support with [Mustache](https://mustache.github.io/)-like placeholders
127
133
 
128
134
  ## Installation
129
135
 
130
- If you're working on Server functionality - first you will need `nodemailer`, although this package is meant to be used with `nodemailer`, it's not added as the dependency, as it not needed by Client, and you're free to choose `nodemailer`'s version to fit your needs:
136
+ To implement Server functionality — as a first step install `nodemailer`, although this package meant to be used with `nodemailer`, it's not added as the dependency, as `nodemailer` not needed by Client, and you're free to choose `nodemailer`'s version to fit your project needs:
131
137
 
132
138
  ```shell
133
139
  npm install --save nodemailer
@@ -151,14 +157,14 @@ Require package:
151
157
  const MailTime = require('mail-time');
152
158
  ```
153
159
 
154
- Create nodemailer's transports (see [nodemailer docs](https://github.com/nodemailer/nodemailer/tree/v2#setting-up)):
160
+ Create nodemailer's transports (see [`nodemailer` docs](https://github.com/nodemailer/nodemailer/tree/v2#setting-up)):
155
161
 
156
162
  ```js
157
163
  const transports = [];
158
164
  const nodemailer = require('nodemailer');
159
165
 
160
166
  // Use DIRECT transport
161
- // To enable sending email from localhost
167
+ // and enable sending email from localhost
162
168
  // install "nodemailer-direct-transport" NPM package:
163
169
  const directTransport = require('nodemailer-direct-transport');
164
170
  const directTransportOpts = {
@@ -204,8 +210,7 @@ transports.push(nodemailer.createTransport({
204
210
  }));
205
211
  ```
206
212
 
207
- Create `mail-time` *Server*, it is able to send and add emails to the queue.
208
- We will need connect to MongoDB first:
213
+ As the next step initiate `mail-time` in the *Server* mode, it will be able to __send__ and __add__ emails to the queue. Connecting to a MongoDB before initiating `new MailTime` instance:
209
214
 
210
215
  ```js
211
216
  const MongoClient = require('mongodb').MongoClient;
@@ -226,7 +231,7 @@ MongoClient.connect(process.env.MONGO_URL, (error, client) => {
226
231
  from(transport) {
227
232
  // To pass spam-filters `from` field should be correctly set
228
233
  // for each transport, check `transport` object for more options
229
- return '"Awesome App" <' + transport.options.from + '>';
234
+ return `"Awesome App" <${transport.options.from}>`;
230
235
  },
231
236
  concatEmails: true, // Concatenate emails to the same addressee
232
237
  concatDelimiter: '<h1>{{{subject}}}</h1>', // Start each concatenated email with it's own subject
@@ -235,7 +240,7 @@ MongoClient.connect(process.env.MONGO_URL, (error, client) => {
235
240
  });
236
241
  ```
237
242
 
238
- Create the *Client* to add emails to queue from other application units, like UI unit:
243
+ Only __one__ `MailTime` *Server* instance required to send email. In the other parts of an app (like UI units or in sub-apps) use `mail-time` in the *Client* mode to __add__ emails to queue:
239
244
 
240
245
  ```js
241
246
  const MongoClient = require('mongodb').MongoClient;
@@ -254,7 +259,7 @@ MongoClient.connect(process.env.MONGO_URL, (error, client) => {
254
259
  });
255
260
  ```
256
261
 
257
- Send email:
262
+ Send email example:
258
263
 
259
264
  ```js
260
265
  mailQueue.sendMail({
@@ -265,33 +270,103 @@ mailQueue.sendMail({
265
270
  });
266
271
  ```
267
272
 
273
+ ### Two `MailTime` instances usage example
274
+
275
+ Create two `MailTime` instances with different settings.
276
+
277
+ ```js
278
+ // USE FOR NON-URGENT EMAILS WHICH IS OKAY TO CONCATENATE
279
+ const mailQueue = new MailTime({
280
+ db: db,
281
+ interval: 35,
282
+ strategy: 'backup',
283
+ failsToNext: 1,
284
+ concatEmails: true,
285
+ concatThrottling: 16,
286
+ zombieTime: 120000
287
+ });
288
+
289
+ // USE FOR TRANSACTIONAL EMAILS AND ALERTS
290
+ const mailInstantQueue = new MailTime({
291
+ db: db,
292
+ prefix: 'instant',
293
+ interval: 2,
294
+ strategy: 'backup',
295
+ failsToNext: 1,
296
+ concatEmails: false,
297
+ zombieTime: 20000
298
+ });
299
+
300
+ mailQueue.sendMail({
301
+ to: 'user@gmail.com',
302
+ subject: 'You\'ve got an email!',
303
+ text: 'Plain text message',
304
+ html: '<h1>HTML</h1><p>Styled message</p>'
305
+ });
306
+
307
+ mailInstantQueue.sendMail({
308
+ to: 'user@gmail.com',
309
+ subject: 'Sign in request',
310
+ text: 'Your OTP login code: xxxx:',
311
+ html: '<h1>Code:</h1><code>XXXX</code>'
312
+ });
313
+ ```
314
+
315
+ ### Passing variables to the template
316
+
317
+ All options passed to the `.sendMail()` method is available inside `text`, `html`, and global templates
318
+
319
+ ```js
320
+ const templates = {
321
+ global: '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>{{subject}}</title></head><body>{{html}}<footer>Message sent to @{{username}} user ({{to}})</footer></body></html>',
322
+ signInCode: {
323
+ text: 'Hello @{{username}}! Here\'s your login code: {{code}}'
324
+ html: `<h1>Sign-in request</h1><p>Hello @{{username}}! Copy your login code below <pre><code>{{code}}</code></pre>`
325
+ }
326
+ }
327
+
328
+ const mailQueue = new MailTime({
329
+ db: db,
330
+ template: templates.global
331
+ });
332
+
333
+ mailQueue.sendMail({
334
+ to: 'user@gmail.com',
335
+ subject: 'Sign-in request',
336
+ username: 'johndoe',
337
+ code: 'XXXXX-YY'
338
+ text: templates.signInCode.text,
339
+ html: templates.signInCode.html
340
+ });
341
+ ```
342
+
268
343
  ## Meteor.js usage:
269
344
 
270
- ### Meteor.js Installation:
345
+ Mail-Time package can be installed and used within [Meteor.js](https://docs.meteor.com/) via [NPM](https://www.npmjs.com/package/mail-time) or [Atmosphere](https://atmospherejs.com/ostrio/mailer)
271
346
 
272
- #### Installation & Import (*via NPM*):
347
+ ### Installation & Import (*via NPM*):
273
348
 
274
- Install NPM *MailTime* package:
349
+ Install [NPM `mail-time` package](https://www.npmjs.com/package/mail-time):
275
350
 
276
351
  ```shell
277
352
  meteor npm install --save mail-time
278
353
  ```
279
354
 
280
- ES6 Import:
355
+ Meteor.js: ES6 Import NPM package:
281
356
 
282
357
  ```js
283
358
  import MailTime from 'mail-time';
284
359
  ```
285
360
 
286
- #### Installation & Import (*via Atmosphere*):
361
+ ### Installation & Import (*via Atmosphere*):
287
362
 
288
- Install Atmosphere *ostrio:mailer* package:
363
+ Install [Atmosphere `ostrio:mailer` package](https://atmospherejs.com/ostrio/mailer):
289
364
 
290
365
  ```shell
291
366
  meteor add ostrio:mailer
292
367
  ```
293
368
 
294
- ES6 Import:
369
+ Meteor.js: ES6 Import atmosphere package:
295
370
 
296
371
  ```js
297
372
  import MailTime from 'meteor/ostrio:mailer';
@@ -299,6 +374,8 @@ import MailTime from 'meteor/ostrio:mailer';
299
374
 
300
375
  ### Usage:
301
376
 
377
+ `mail-time` package usage examples in Meteor.js
378
+
302
379
  ```js
303
380
  import { MongoInternals } from 'meteor/mongo';
304
381
 
@@ -338,6 +415,8 @@ const mailQueue = new MailTime({
338
415
 
339
416
  ## API
340
417
 
418
+ All available constructor options and `.sendMail()` method API overview
419
+
341
420
  ### `new MailTime(opts)` constructor
342
421
 
343
422
  - `opts` {*Object*} - Configuration object
@@ -373,7 +452,7 @@ const mailQueue = new MailTime({
373
452
 
374
453
  ### `static MailTime.Template`
375
454
 
376
- Simple and bulletproof HTML template, see its [source](https://github.com/veliovgroup/Mail-Time/blob/master/template.html). Usage:
455
+ Simple and bulletproof HTML template, see [its source](https://github.com/veliovgroup/Mail-Time/blob/master/template.html). Usage example:
377
456
 
378
457
  ```js
379
458
  const MailTime = require('mail-time');
@@ -394,6 +473,8 @@ mailQueue.sendMail({
394
473
 
395
474
  ### Template Example
396
475
 
476
+ Pass custom template via `template` property to `.sendMail()` method
477
+
397
478
  ```js
398
479
  mailQueue.sendMail({
399
480
  to: 'user@gmail.com',
@@ -441,6 +522,10 @@ DEBUG="true" EMAIL_DOMAIN="example.com" MONGO_URL="mongodb://127.0.0.1:27017/met
441
522
 
442
523
  ## Support this project:
443
524
 
444
- - [Sponsor via GitHub](https://github.com/sponsors/dr-dimitru) — support open source contributions on a regular basis
445
- - [Support via PayPal](https://paypal.me/veliovgroup) — support open source contributions once
525
+ - Star on [GitHub](https://github.com/veliovgroup/mail-time)
526
+ - Star on [NPM](https://www.npmjs.com/package/mail-time)
527
+ - Star on [Atmosphere](https://atmospherejs.com/ostrio/mailer)
528
+ - [Sponsor maintainer via GitHub](https://github.com/sponsors/dr-dimitru) — support open source with one-time contribution or on a regular basis
529
+ - [Sponsor veliovgroup via GitHub](https://github.com/sponsors/veliovgroup) — support company behind this package
530
+ - [Support via PayPal](https://paypal.me/veliovgroup) — support our open source contributions
446
531
  - Use [ostr.io](https://ostr.io) — [Monitoring](https://snmp-monitoring.com), [Analytics](https://ostr.io/info/web-analytics), [WebSec](https://domain-protection.info), [Web-CRON](https://web-cron.info) and [Pre-rendering](https://prerendering.com) for a website
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
- const JoSk = require('josk');
2
- const NoOp = () => {};
1
+ const JoSk = require('josk');
2
+ const noop = () => {};
3
3
  const merge = require('deepmerge');
4
4
  const _debug = (isDebug, ...args) => {
5
5
  if (isDebug) {
@@ -142,9 +142,9 @@ module.exports = class MailTime {
142
142
  return false;
143
143
  })();
144
144
 
145
- this.concatEmails = (opts.concatEmails !== true) ? false : true;
146
- this.concatSubject = (opts.concatSubject && typeof opts.concatSubject === 'string') ? opts.concatSubject : 'Multiple notifications';
147
- this.concatDelimiter = (opts.concatDelimiter && typeof opts.concatDelimiter === 'string') ? opts.concatDelimiter : '<hr>';
145
+ this.concatEmails = (opts.concatEmails !== true) ? false : true;
146
+ this.concatSubject = (opts.concatSubject && typeof opts.concatSubject === 'string') ? opts.concatSubject : 'Multiple notifications';
147
+ this.concatDelimiter = (opts.concatDelimiter && typeof opts.concatDelimiter === 'string') ? opts.concatDelimiter : '<hr>';
148
148
  this.concatThrottling = ((opts.concatThrottling && !isNaN(opts.concatThrottling)) ? parseInt(opts.concatThrottling) : 60) * 1000;
149
149
 
150
150
  if (this.concatThrottling < 2048) {
@@ -163,7 +163,7 @@ module.exports = class MailTime {
163
163
  }
164
164
 
165
165
  this.collection = opts.db.collection('__mailTimeQueue__' + this.prefix);
166
- this.collection.createIndex({ to: 1, isSent: 1 }, (indexError) => {
166
+ this.collection.createIndex({ to: 1, isSent: 1, sendAt: 1 }, (indexError) => {
167
167
  if (indexError) {
168
168
  _logError('[createIndex]', indexError);
169
169
  }
@@ -192,7 +192,7 @@ module.exports = class MailTime {
192
192
  // mailOptions.Other nodeMailer `sendMail` options...
193
193
 
194
194
  if (this.type === 'server') {
195
- this.scheduler = new JoSk({
195
+ const scheduler = new JoSk({
196
196
  db: opts.db,
197
197
  debug: this.debug,
198
198
  prefix: `mailTimeQueue${this.prefix}`,
@@ -202,7 +202,7 @@ module.exports = class MailTime {
202
202
  maxRevolvingDelay: this.maxRevolvingDelay
203
203
  });
204
204
 
205
- this.scheduler.setInterval(this.___send.bind(this), this.revolvingInterval, `mailTimeQueue${this.prefix}`);
205
+ scheduler.setInterval(this.___send.bind(this), this.revolvingInterval, `mailTimeQueue${this.prefix}`);
206
206
  }
207
207
  }
208
208
 
@@ -272,14 +272,13 @@ module.exports = class MailTime {
272
272
  return _mailOpts;
273
273
  }
274
274
 
275
- /*
276
- @memberOf MailTime
277
- @name ___send
278
- @param ready {Function} - See JoSk NPM package
279
- @returns {void}
275
+ /**
276
+ * @memberOf MailTime
277
+ * @name ___send
278
+ * @param ready {Function} - See JoSk NPM package
279
+ * @returns {void}
280
280
  */
281
281
  ___send(ready) {
282
- let finished = 0;
283
282
  const cursor = this.collection.find({
284
283
  isSent: false,
285
284
  sendAt: {
@@ -299,113 +298,96 @@ module.exports = class MailTime {
299
298
  }
300
299
  });
301
300
 
302
- cursor.count((countError, count) => {
303
- if (countError) {
304
- ready();
305
- cursor.close();
306
- _logError('[___send] [count] [countError]', countError);
307
- return;
308
- }
309
-
310
- if (count) {
311
- cursor.forEach((task) => {
312
- this.collection.updateOne({
313
- _id: task._id
314
- }, {
315
- $set: {
316
- isSent: true
317
- },
318
- $inc: {
319
- tries: 1
320
- }
321
- }, (updateError) => {
322
- if (count === ++finished) {
323
- ready();
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;
324
321
  }
322
+ transportIndex = this.transport;
323
+ transport = this.transports[this.transport];
324
+ } else {
325
+ transportIndex = task.transport;
326
+ transport = this.transports[task.transport];
327
+ }
325
328
 
326
- if (updateError) {
327
- this.___handleError(task, updateError, {});
328
- } else {
329
- let transport;
330
- let transportIndex;
331
- if (this.strategy === 'balancer') {
332
- this.transport = this.transport + 1;
333
- if (this.transport >= this.transports.length) {
334
- this.transport = 0;
335
- }
336
- transportIndex = this.transport;
337
- transport = this.transports[this.transport];
338
- } else {
339
- transportIndex = task.transport;
340
- transport = this.transports[task.transport];
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;
341
337
  }
342
338
 
343
- try {
344
- const _mailOpts = this.___compileMailOpts(transport, task);
345
-
346
- _debug(this.debug, '[sendMail] [sending] To:', _mailOpts.to);
347
- transport.sendMail(_mailOpts, (error, info) => {
348
- if (error) {
349
- this.___handleError(task, error, info);
350
- return;
351
- }
352
-
353
- if (info.accepted && !info.accepted.length) {
354
- this.___handleError(task, 'Message not accepted or Greeting never received', info);
355
- return;
356
- }
357
-
358
- _debug(this.debug, `email successfully sent, attempts: #${task.tries}, transport #${transportIndex} to: `, _mailOpts.to);
359
- if (this.keepHistory) {
360
- this.___triggerCallbacks(void 0, task, info);
361
- } else {
362
- this.collection.deleteOne({ _id: task._id }, () => {
363
- this.___triggerCallbacks(void 0, task, info);
364
- });
365
- }
366
-
367
- return;
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);
368
350
  });
369
- } catch (e) {
370
- _logError('Exception during runtime:', e);
371
- this.___handleError(task, e, {});
372
351
  }
373
- }
374
- });
375
- }, (forEachError) => {
376
- cursor.close();
377
- if (forEachError) {
378
- _logError('[___send] [forEach] [forEachError]', forEachError);
352
+
353
+ return;
354
+ });
355
+ } catch (e) {
356
+ _logError('Exception during runtime:', e);
357
+ this.___handleError(task, e, {});
379
358
  }
380
- });
381
- } else {
382
- ready();
383
- cursor.close();
359
+ }
360
+ });
361
+ }, (forEachError) => {
362
+ ready();
363
+ cursor.close();
364
+ if (forEachError) {
365
+ _logError('[___send] [forEach] [forEachError]', forEachError);
384
366
  }
385
367
  });
386
368
  }
387
369
 
388
- /*
389
- @memberOf MailTime
390
- @name send
391
- @description alias of `sendMail`
392
- @returns {void}
370
+ /**
371
+ * @memberOf MailTime
372
+ * @name send
373
+ * @description alias of `sendMail`
374
+ * @returns {void}
393
375
  */
394
376
  send(opts, callback) {
395
377
  this.sendMail(opts, callback);
396
378
  }
397
379
 
398
- /*
399
- @memberOf MailTime
400
- @name sendMail
401
- @param opts {Object} - Letter options with next properties:
402
- @param opts.sendAt {Date} - When email should be sent
403
- @param opts.template {String} - Template string
404
- @param opts[key] {mix} - Other MailOptions according to NodeMailer lib documentation
405
- @param callback {Function} - [OPTIONAL] Callback function
406
- @returns {void}
380
+ /**
381
+ * @memberOf MailTime
382
+ * @name sendMail
383
+ * @param opts {Object} - Letter options with next properties:
384
+ * @param opts.sendAt {Date} - When email should be sent
385
+ * @param opts.template {String} - Template string
386
+ * @param opts[key] {mix} - Other MailOptions according to NodeMailer lib documentation
387
+ * @param callback {Function} - [OPTIONAL] Callback function
388
+ * @returns {void}
407
389
  */
408
- sendMail(opts = {}, callback = NoOp) {
390
+ sendMail(opts = {}, callback = noop) {
409
391
  _debug(this.debug, '[sendMail] [attempt] To:', opts.to);
410
392
  if (!opts.html && !opts.text) {
411
393
  throw new Error('`html` nor `text` field is presented, at least one of those fields is required');
@@ -423,8 +405,8 @@ module.exports = class MailTime {
423
405
  opts.concatSubject = false;
424
406
  }
425
407
 
426
- let _sendAt = opts.sendAt;
427
- const _template = opts.template;
408
+ let _sendAt = opts.sendAt;
409
+ const _template = opts.template;
428
410
  const _concatSubject = opts.concatSubject;
429
411
  delete opts.sendAt;
430
412
  delete opts.template;
@@ -437,7 +419,10 @@ module.exports = class MailTime {
437
419
  if (this.concatEmails) {
438
420
  this.collection.findOne({
439
421
  to: opts.to,
440
- isSent: false
422
+ isSent: false,
423
+ sendAt: {
424
+ $lte: new Date(+_sendAt + this.concatThrottling)
425
+ }
441
426
  }, {
442
427
  projection: {
443
428
  _id: 1,
@@ -500,13 +485,13 @@ module.exports = class MailTime {
500
485
  return;
501
486
  }
502
487
 
503
- /*
504
- @memberOf MailTime
505
- @name ___handleError
506
- @param task {Object} - Email task record form Mongo
507
- @param error {mix} - Error String/Object/Error
508
- @param info {Object} - Info object returned from nodemailer
509
- @returns {void}
488
+ /**
489
+ * @memberOf MailTime
490
+ * @name ___handleError
491
+ * @param task {Object} - Email task record form Mongo
492
+ * @param error {mix} - Error String/Object/Error
493
+ * @param info {Object} - Info object returned from nodemailer
494
+ * @returns {void}
510
495
  */
511
496
  ___handleError(task, error, info) {
512
497
  if (!task) {
@@ -544,16 +529,16 @@ module.exports = class MailTime {
544
529
  }
545
530
  }
546
531
 
547
- /*
548
- @memberOf MailTime
549
- @name ___addToQueue
550
- @param opts {Object} - Letter options with next properties:
551
- @param opts.sendAt {Date} - When email should be sent
552
- @param opts.template {String} - Email template
553
- @param opts.mailOptions {Object} - MailOptions according to NodeMailer lib
554
- @param opts.concatSubject {String} - Email subject used when sending multiple concatenated emails
555
- @param callback {Function} - [OPTIONAL] Callback function
556
- @returns {void}
532
+ /**
533
+ * @memberOf MailTime
534
+ * @name ___addToQueue
535
+ * @param opts {Object} - Letter options with next properties:
536
+ * @param opts.sendAt {Date} - When email should be sent
537
+ * @param opts.template {String} - Email template
538
+ * @param opts.mailOptions {Object} - MailOptions according to NodeMailer lib
539
+ * @param opts.concatSubject {String} - Email subject used when sending multiple concatenated emails
540
+ * @param callback {Function} - [OPTIONAL] Callback function
541
+ * @returns {void}
557
542
  */
558
543
  ___addToQueue(opts, callback) {
559
544
  _debug(this.debug, '[sendMail] [adding to queue] To:', opts.mailOptions.to);
@@ -587,16 +572,16 @@ module.exports = class MailTime {
587
572
  });
588
573
  }
589
574
 
590
- /*
591
- @memberOf MailTime
592
- @name ___render
593
- @param string {String} - Template with Spacebars/Blaze/Mustache-like placeholders
594
- @param replacements {Object} - Blaze/Mustache-like helpers Object
595
- @returns {String}
575
+ /**
576
+ * @memberOf MailTime
577
+ * @name ___render
578
+ * @param string {String} - Template with Spacebars/Blaze/Mustache-like placeholders
579
+ * @param replacements {Object} - Blaze/Mustache-like helpers Object
580
+ * @returns {String}
596
581
  */
597
582
  ___render(_string, replacements) {
598
583
  let i;
599
- let string = _string;
584
+ let string = _string;
600
585
  const matchHTML = string.match(/\{{3}\s?([a-zA-Z0-9\-\_]+)\s?\}{3}/g);
601
586
  if (matchHTML) {
602
587
  for (i = 0; i < matchHTML.length; i++) {
@@ -617,13 +602,13 @@ module.exports = class MailTime {
617
602
  return string;
618
603
  }
619
604
 
620
- /*
621
- @memberOf MailTime
622
- @name ___triggerCallbacks
623
- @param error {mix} - Error thrown during sending email
624
- @param task {Object} - Task record from mongodb
625
- @param info {Object} - Info object returned from NodeMailer's `sendMail` method
626
- @returns {void 0}
605
+ /**
606
+ * @memberOf MailTime
607
+ * @name ___triggerCallbacks
608
+ * @param error {mix} - Error thrown during sending email
609
+ * @param task {Object} - Task record from mongodb
610
+ * @param info {Object} - Info object returned from NodeMailer's `sendMail` method
611
+ * @returns {void 0}
627
612
  */
628
613
  ___triggerCallbacks(error, task, info) {
629
614
  const _id = task._id.toHexString();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mail-time",
3
- "version": "1.2.6",
4
- "description": "Bulletproof email queue on top of NodeMailer with support of multiple clusters and servers setup",
3
+ "version": "1.3.0",
4
+ "description": "Bulletproof email queue on top of NodeMailer for a single and multi-server setups",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "mocha ./test/npm.js"
@@ -49,15 +49,15 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "deepmerge": "^4.2.2",
52
- "josk": "^2.4.0"
52
+ "josk": "^3.0.0"
53
53
  },
54
54
  "devDependencies": {
55
- "bson": "^4.2.2",
56
- "bson-ext": "^2.0.5",
57
- "chai": "^4.3.0",
58
- "mocha": "^8.2.1",
59
- "mongodb": "^3.6.4",
60
- "nodemailer": "^6.4.17",
61
- "nodemailer-direct-transport": "^3.3.2"
55
+ "bson": "^4.6.4",
56
+ "bson-ext": "^4.0.2",
57
+ "chai": "^4.3.6",
58
+ "mocha": "^10.0.0",
59
+ "mongodb": "^4.6.0",
60
+ "nodemailer": "^6.7.5",
61
+ "nodemailer-direct-transport": "^3.0.7"
62
62
  }
63
63
  }
package/CHANGELOG.md DELETED
@@ -1 +0,0 @@
1
- For full changelog see [releases](https://github.com/veliovgroup/Mail-Time/releases) in GitHub
package/HISTORY.md DELETED
@@ -1 +0,0 @@
1
- For full history see [releases](https://github.com/veliovgroup/Mail-Time/releases) in GitHub