{"id":13753194,"url":"https://github.com/VeliovGroup/Mail-Time","last_synced_at":"2025-05-09T20:35:01.895Z","repository":{"id":19938868,"uuid":"88346002","full_name":"veliovgroup/mail-time","owner":"veliovgroup","description":"📮 Email queue extending NodeMailer with multi SMTP transports and horizontally scaled applications support","archived":false,"fork":false,"pushed_at":"2025-03-28T22:17:06.000Z","size":893,"stargazers_count":144,"open_issues_count":2,"forks_count":23,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-05-06T13:57:45.576Z","etag":null,"topics":["balancer","cluster","email","email-sender","mail","mail-time","mailer","microservice","nodejs","npm","server","smtp"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/mail-time","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/veliovgroup.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"dr-dimitru","custom":"https://paypal.me/veliovgroup"}},"created_at":"2017-04-15T12:39:09.000Z","updated_at":"2025-03-23T09:29:57.000Z","dependencies_parsed_at":"2024-02-22T01:43:08.699Z","dependency_job_id":"a9a46031-e296-458f-acdf-250b07a73008","html_url":"https://github.com/veliovgroup/mail-time","commit_stats":{"total_commits":131,"total_committers":9,"mean_commits":"14.555555555555555","dds":0.1145038167938931,"last_synced_commit":"666713ca8dc0327c426214a90a912c8a23ac747a"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2Fmail-time","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2Fmail-time/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2Fmail-time/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veliovgroup%2Fmail-time/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/veliovgroup","download_url":"https://codeload.github.com/veliovgroup/mail-time/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253321839,"owners_count":21890476,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["balancer","cluster","email","email-sender","mail","mail-time","mailer","microservice","nodejs","npm","server","smtp"],"created_at":"2024-08-03T09:01:18.133Z","updated_at":"2025-05-09T20:34:56.865Z","avatar_url":"https://github.com/veliovgroup.png","language":"JavaScript","readme":"[![support](https://img.shields.io/badge/support-GitHub-white)](https://github.com/sponsors/dr-dimitru)\n[![support](https://img.shields.io/badge/support-PayPal-white)](https://paypal.me/veliovgroup)\n\u003ca href=\"https://ostr.io/info/built-by-developers-for-developers?ref=github-mail-time-repo-top\"\u003e\u003cimg src=\"https://ostr.io/apple-touch-icon-60x60.png\" height=\"20\"\u003e\u003c/a\u003e\n\u003ca href=\"https://meteor-files.com/?ref=github-mail-time-repo-top\"\u003e\u003cimg src=\"https://meteor-files.com/apple-touch-icon-60x60.png\" height=\"20\"\u003e\u003c/a\u003e\n\n# MailTime\n\n\"Mail-Time\" is NPM package for mail queue management. Build on top of the [`nodemailer`](https://github.com/nodemailer/nodemailer) package. Mail-Time made for single-server and horizontally scaled multi-server setups in mind.\n\nEvery `MailTime` instance can have `type` configured as *Server* or *Client*. *Server* type of `MailTime` is great for creating an emailing micro-service app.\n\nThe main difference between *Server* and *Client* `type` is that the *Server* handles the queue and __sends__ email. While the *Client* only __adds__ emails into the queue.\n\n## ToC\n\n- [How it works?](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#how-it-works)\n  - [With single SMTP](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#single-point-of-failure)\n  - [With multiple SMTP](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#multiple-smtp-providers)\n  - [For horizontally-scaled apps](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#sending-emails-from-cluster-of-servers)\n- [Features](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#features)\n- [Installation](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#installation)\n- [Meteor.js usage](https://github.com/veliovgroup/mail-time/blob/master/docs/meteor.md)\n- [Usage examples](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#basic-usage)\n  - [Require/Import `mail-time`](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#1-require-package)\n  - [Create NodeMailer's transports](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#2-create-nodemailers-transports)\n  - [Initiate `mail-time`](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#3-initiate-mail-time)\n    - [Connect to Redis](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#3a-initiate-and-connect-to-redis)\n    - [Connect to MongoDB](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#3b-initiate-and-connect-to-mongodb)\n    - [Initiate as *Client* instance](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#3c-optionally-create-client-type-of-mailtime)\n  - [Initiate two `MailTime` instances within the single app](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#two-mailtime-instances-usage-example)\n  - [Use templates](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#passing-variables-to-the-template)\n  - Different storage configurations:\n    - [Use MongoDB for queue and scheduler](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#using-mongodb-for-queue-and-scheduler)\n    - [Use MongoDB for queue and Redis for scheduler](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#using-mongodb-for-queue-and-redis-for-scheduler)\n    - [Use Redis for queue and MongoDB for scheduler](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#using-redis-for-queue-and-mongodb-for-scheduler)\n    - [Use Redis for queue and scheduler](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#using-redis-for-queue-and-scheduler)\n- [API](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#api)\n  - [`new MailTime` *Constructor*](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#new-mailtimeopts-constructor)\n  - [`new RedisQueue` *Constructor*](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#new-redisqueueopts-constructor)\n  - [`new MongoQueue` *Constructor*](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#new-mongoqueueopts-constructor)\n  - [`.sendMail()`](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#sendmailopts)\n  - [`.cancelMail()`](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#cancelmailuuid)\n  - [`.ping()`](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#ping)\n  - [Default Template](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#static-mailtimetemplate)\n- [Custom Templates](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#template-example)\n- [Running tests](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#testing)\n\n## Main features:\n\n- 👨‍🔬 ~94% tests coverage;\n- 📦 Two simple dependencies, written from scratch for top performance;\n- 🏢 Synchronize email queue across multiple (horizontally scaled) servers;\n- 💪 Bulletproof design, built-in retries.\n\n## How does it work?\n\nRedundant solution for email transmission.\n\n### Single point of failure\n\nIssue - mitigate a single point of failure via persistent queue and re-send attempts\n\n```ascii\n|----------------|         |------|         |------------------|\n|  Other mailer  | ------\u003e | SMTP | ------\u003e |  ^_^ Happy user  |\n|----------------|         |------|         |------------------|\n\nThe scheme above will work as long as SMTP service is available\nor connection between your server and SMPT is up. Once network\nfailure occurs or SMTP service is down - users won't be happy\n\n|----------------|  \\ /    |------|         |------------------|\n|  Other mailer  | --X---\u003e | SMTP | ------\u003e | 0_o Disappointed |\n|----------------|  / \\    |------|         |------------------|\n                     ^- email lost in vain\n\nSingle SMTP solution may work in case of network or other failures\nAs long as MailTime has not received confirmation what email is sent\nit will keep the letter in the queue and retry to send it again\n\n|----------------|    /    |------|         |------------------|\n|   Mail Time    | --X---\u003e | SMTP | ------\u003e |  ^_^ Happy user  |\n|---^------------|  /      |------|         |------^-----------|\n     \\-------------/ ^- We will try later         /\n      \\- put it back into queue                  /\n       \\----------Once connection is back ------/\n```\n\n### Multiple SMTP providers\n\nRotate email transports by using multiple SMTP providers. *MailTime* support two strategies `backup` (*rotate when failed*) and `balancer` (*round-robin rotation*)\n\n```ascii\n                           |--------|\n                     /--X--| SMTP 1 |\n                    /   ^  |--------|\n                   /    \\--- Retry with next provider\n|----------------|/        |--------|         |------------------|\n|   Mail Time    | ---X--\u003e | SMTP 2 |      /-\u003e|  ^_^ Happy user  |\n|----------------|\\   ^    |--------|     /   |------------------|\n                   \\  \\--- Retry         /\n                    \\      |--------|   /\n                     \\----\u003e| SMTP 3 |--/\n                           |--------|\n```\n\n### Sending emails from cluster of servers\n\nIt is common to have horizontally scaled \"Cluster\" of servers for load-balancing and for durability.\n\nMost 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. But in \"Cluster\" implementation — each server will attempt to send the same email. *MailTime* built to avoid sending the same email multiple times to a user from horizontally scaled applications.\n\nFor the maximum durability and agility each Application Server can run *MailTime* in the \"Server\" mode:\n\n```ascii\n|===================THE=CLUSTER===================| |=QUEUE=|\n| |----------|     |----------|     |----------|  | |       |   |--------|\n| |   App    |     |   App    |     |   App    |  | |       |--\u003e| SMTP 1 |------\\\n| | Server 1 |     | Server 2 |     | Server 3 |  | |       |   |--------|       \\\n| |-----\\----|     |----\\-----|     |----\\-----|  | |       |                |-------------|\n|        \\---------------\\----------------\\----------\u003e      |   |--------|   |     ^_^     |\n|                                                 | |       |--\u003e| SMTP 2 |--\u003e| Happy users |\n| Each \"App Server\"                               | |       |   |--------|   |-------------|\n| runs MailTime as a \"Server\"                     | |       |                    /\n| for the maximum durability                      | |       |   |--------|      /\n|                                                 | |       |--\u003e| SMTP 3 |-----/\n|                                                 | |       |   |--------|\n|=================================================| |=======|\n```\n\nTo split roles *MailTime* can run on a dedicated machine as micro-service. This case is great for private email servers with implemented authentication via rDNS and PTR records:\n\n```ascii\n|===================THE=CLUSTER===================| |=QUEUE=| |===Mail=Time===|\n| |----------|     |----------|     |----------|  | |       | |               |   |--------|\n| |   App    |     |   App    |     |   App    |  | |       | | Micro-service |--\u003e| SMTP 1 |------\\\n| | Server 1 |     | Server 2 |     | Server 3 |  | |       | | running       |   |--------|       \\\n| |-----\\----|     |----\\-----|     |----\\-----|  | |       | | MailTime as   |                |-------------|\n|        \\---------------\\----------------\\----------\u003e      | | \"Server\" only |   |--------|   |     ^_^     |\n|                                                 | |       | | sending       |--\u003e| SMTP 2 |--\u003e| Happy users |\n| Each \"App Server\" runs MailTime as              | |       | | emails        |   |--------|   |-------------|\n| a \"Client\" only placing emails to the queue.    | |    \u003c--------            |                    /\n|                                                 | |    --------\u003e            |   |--------|      /\n|                                                 | |       | |               |--\u003e| SMTP 3 |-----/\n|                                                 | |       | |               |   |--------|\n|=================================================| |=======| |===============|\n```\n\n## Features\n\n- __Email Queue__ - Managed via MongoDB, Redis, or [Custom Queue](https://github.com/veliovgroup/mail-time/blob/master/docs/queue-api.md). Storage-based queue will survive server reboots and failures\n- __Made for horizontally scaled multi-server setups__ - *MailTime* is made to run in multi-server environments, like \"Clusters\", multiple app instances, load balanced solutions, and replications. *MailTime* is the perfect fit for applications scaled on a single machine, multiple virtual servers, multiple \"bare metal\" servers, within single or multiple data centers\n- __Email concatenation__ - Reduce amount of sent emails to a single user with concatenation, and avoid mistakenly duplicated emails. When \"email concatenation\" is enabled the same emails (*checked by addressee and content*) won't be sent twice. If emails are sent multiple times, due to issues in logic or application failures, - enable \"email concatenation\" to solve this behavior\n- __Multiple NodeMailer/SMTP transports__ — Support for multiple SMPT transports implemented in two modes - `backup` and `balancing`. Use this feature to reduce the cost of SMTP services and add extra layer of durability. When one of the transports is failing to send an email — `mail-time` will switch to the next one\n- __Sending retries__ — Built-in retries for failed to send emails due to network or other failures\n- __Templating__ — Built with support of [Mustache](https://mustache.github.io/)-like placeholders, see [templating docs](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#passing-variables-to-the-template)\n\n## Installation\n\nTo implement Server functionality — begin with installing `nodemailer`, although this package meant to be used with `nodemailer`, it's not added as the dependency, as `nodemailer` not needed by Client, and to give freedom to choose `nodemailer`'s version to fit every project needs:\n\n```shell\nnpm install --save nodemailer\n```\n\nInstall *MailTime* package:\n\n```shell\n# for node@\u003e=14.20.0\nnpm install --save mail-time\n\n# for node@\u003c14.20.0\nnpm install --save mail-time@=1.3.4\n\n# for node@\u003c8.9.0\nnpm install --save mail-time@=0.1.7\n```\n\n## Basic usage\n\nSetup Nodemailer's transports, Queue storage, and *MailTime* instance\n\n### Steps to get started\n\nSee steps 1-4 below to learn about different parts of *MailTime* library and how it can get used. From configuration options to sending email\n\n1. [Require `mail-time` package](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#1-require-package)\n2. [Create NodeMailer's transports](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#2-create-nodemailers-transports)\n3. [Initiate `mail-time` *server*](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#3-initiate-mail-time)\n   - a. [Connect to Redis](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#3a-initiate-and-connect-to-redis); Or\n   - b. [Connect to MongoDB](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#3b-initiate-and-connect-to-mongodb); And\n   - c. [*optionally*] [initiate `mail-time` as *client*](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#3c-optionally-create-client-type-of-mailtime)\n4. [Start sending emails](https://github.com/veliovgroup/mail-time?tab=readme-ov-file#4-send-email)\n\n#### 1. Require package\n\n```js\n// import as ES Module\nimport { MailTime, MongoQueue, RedisQueue } from 'mail-time';\n\n// require as CommonJS\nconst { MailTime, MongoQueue, RedisQueue } = require('mail-time');\n```\n\n#### 2. Create NodeMailer's transports\n\nFor compatibility and flexibility *MailTime* has no dependency on `nodemailer` it should be installed and imported manually. Create one or more \"SMTP transports\" before initializing new *MailTime* instance.\n\nFor details and full list of options available in `.createTransport()` see [`nodemailer` docs](https://nodemailer.com/smtp/)\n\n```js\n// transports.js\nimport nodemailer from 'nodemailer';\n// Use DIRECT transport\n// and enable sending email from localhost\n// install \"nodemailer-direct-transport\" NPM package:\nimport directTransport from 'nodemailer-direct-transport';\n\nconst transports = [];\nconst directTransportOpts = {\n  pool: false,\n  direct: true,\n  name: 'mail.example.com',\n  from: 'no-reply@example.com',\n};\ntransports.push(nodemailer.createTransport(directTransport(directTransportOpts)));\n// IMPORTANT: Add `.options` to a newly created transport,\n// this is necessary to make sure options are available to MailTime package:\ntransports[0].options = directTransportOpts;\n\n// Private SMTP\ntransports.push(nodemailer.createTransport({\n  host: 'smtp.example.com',\n  from: 'no-reply@example.com',\n  auth: {\n    user: 'no-reply',\n    pass: 'xxx'\n  },\n}));\n\n// Google Apps SMTP\ntransports.push(nodemailer.createTransport({\n  host: 'smtp.gmail.com',\n  from: 'no-reply@mail.example.com',\n  auth: {\n    user: 'no-reply@mail.example.com',\n    pass: 'xxx'\n  },\n}));\n\n// Mailing service (SparkPost as example)\ntransports.push(nodemailer.createTransport({\n  host: 'smtp.sparkpostmail.com',\n  port: 587,\n  from: 'no-reply@mail2.example.com',\n  auth: {\n    user: 'SMTP_Injection',\n    pass: 'xxx'\n  },\n}));\n\nexport { transports };\n```\n\n#### 3. Initiate `mail-time`\n\nCreate new instance of *MailTime* in the *Server* mode, — it will be able to __send__ and __add__ emails to the queue.\n\n#### 3a. Initiate and connect to Redis\n\nConnecting to Redis before initiating `new MailTime` instance:\n\n```js\n// mail-queue.js\nimport { MailTime, RedisQueue } from 'mail-time';\nimport { createClient } from 'redis';\nimport { transports } from './transports.js';\n\n// Use REDIS_URL environment variable to store connection string to MongoDB\n// example: \"REDIS_URL=redis://127.0.0.1:6379/myapp node mail-micro-service.js\"\nconst redisClient = await createClient({ url: process.env.REDIS_URL }).connect();\nconst mailQueue = new MailTime({\n  transports,\n  queue: new MongoQueue({\n    client: redisClient,\n  }),\n  josk: {\n    adapter: {\n      type: 'redis',\n      client: redisClient,\n    }\n  },\n  template: MailTime.Template // Use default template\n  from(transport) {\n    // To pass spam-filters `from` field should be correctly set\n    // for each transport, check `transport` object for more options\n    return `\"Awesome App\" \u003c${transport.options.from}\u003e`;\n  },\n  onError(error, email, details) {\n    console.log(`Email \"${email.mailOptions.subject}\" wasn't sent to ${email.mailOptions.to}`, error, details);\n  },\n  onSent(email, details) {\n    console.log(`Email \"${email.mailOptions.subject}\" successfully sent to ${email.mailOptions.to}`, details);\n  },\n});\n\nexport { mailQueue };\n```\n\n#### 3b. Initiate and connect to MongoDB\n\nConnecting to MongoDB before initiating `new MailTime` instance:\n\n```js\n// mail-queue.js\nimport { MailTime, MongoQueue } from 'mail-time';\nimport { MongoClient } from 'mongodb';\nimport { transports } from './transports.js';\n\n// Use MONGO_URL environment variable to store connection string to MongoDB\n// example: \"MONGO_URL=mongodb://127.0.0.1:27017/myapp node mail-micro-service.js\"\nconst mongodb = (await MongoClient.connect(process.env.MONGO_URL)).db('database');\nconst mailQueue = new MailTime({\n  transports,\n  queue: new MongoQueue({\n    db: mongodb,\n  }),\n  josk: {\n    adapter: {\n      type: 'mongo',\n      db: mongodb,\n    }\n  },\n  template: MailTime.Template // Use default template\n  from(transport) {\n    // To pass spam-filters `from` field should be correctly set\n    // for each transport, check `transport` object for more options\n    return `\"Awesome App\" \u003c${transport.options.from}\u003e`;\n  },\n  onError(error, email, details) {\n    console.log(`Email \"${email.mailOptions.subject}\" wasn't sent to ${email.mailOptions.to}`, error, details);\n  },\n  onSent(email, details) {\n    console.log(`Email \"${email.mailOptions.subject}\" successfully sent to ${email.mailOptions.to}`, details);\n  },\n});\n\nexport { mailQueue };\n```\n\n#### 3c. Optionally create *Client* type of *MailTime*\n\nOnly __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\n\n```js\n// mail-queue.js\nimport { MailTime, RedisQueue } from 'mail-time';\nimport { createClient } from 'redis';\n\nconst mailQueue = new MailTime({\n  type: 'client',\n  queue: new RedisQueue({\n    client: await createClient({ url: 'redis://url' }).connect()\n  }),\n});\n\nexport { mailQueue };\n```\n\n#### 4. Send email\n\nImport created `mailQueue` where needed and call `.sendMail()` method. See [NodeMailer's message configuration documentation](https://nodemailer.com/message/) for details\n\n```js\nimport { mailQueue } from './mail-queue.js';\n\nawait mailQueue.sendMail({\n  to: 'user@gmail.com',\n  subject: 'You\\'ve got an email!',\n  text: 'Plain text message',\n  html: '\u003ch1\u003eHTML\u003c/h1\u003e\u003cp\u003eStyled message\u003c/p\u003e'\n});\n```\n\n### Using MongoDB for queue and scheduler\n\n*MailTime* uses separate storage for Queue management and Scheduler. In the example below MongoDB is used for both\n\n```js\nimport { MailTime, MongoQueue } from 'mail-time';\nimport { MongoClient } from 'mongodb';\nimport { transports } from './transports.js';\n\nconst db = (await MongoClient.connect('mongodb://url')).db('database');\nconst mailQueue = new MailTime({\n  queue: new MongoQueue({\n    db: db,\n  }),\n  josk: {\n    adapter: {\n      type: 'mongo',\n      db: db,\n    }\n  },\n  transports,\n  from(transport) {\n    // To pass spam-filters `from` field should be correctly set\n    // for each transport, check `transport` object for more options\n    return `\"Awesome App\" \u003c${transport.options.from}\u003e`;\n  }\n});\n```\n\n### Using MongoDB for queue and Redis for scheduler\n\n*MailTime* uses separate storage for Queue management and Scheduler. In the example below MongoDB is used for queue and Redis is used for scheduler\n\n```js\nimport { MailTime, MongoQueue } from 'mail-time';\nimport { MongoClient } from 'mongodb';\nimport { createClient } from 'redis';\nimport { transports } from './transports.js';\n\nconst mailQueue = new MailTime({\n  queue: new MongoQueue({\n    db: (await MongoClient.connect('mongodb://url')).db('database'),\n  }),\n  josk: {\n    adapter: {\n      type: 'redis',\n      client: await createClient({ url: 'redis://url' }).connect(),\n    }\n  },\n  transports,\n  from(transport) {\n    return `\"Awesome App\" \u003c${transport.options.from}\u003e`;\n  }\n});\n```\n\n### Using Redis for queue and MongoDB for scheduler\n\n*MailTime* uses separate storage for Queue management and Scheduler. In the example below Redis is used for queue and MongoDB is used for scheduler\n\n```js\nimport { MailTime, RedisQueue } from 'mail-time';\nimport { MongoClient } from 'mongodb';\nimport { createClient } from 'redis';\nimport { transports } from './transports.js';\n\nconst mailQueue = new MailTime({\n  queue: new RedisQueue({\n    client: await createClient({ url: 'redis://url' }).connect(),\n  }),\n  josk: {\n    adapter: {\n      type: 'mongo',\n      db: (await MongoClient.connect('mongodb://url')).db('database'),\n    }\n  },\n  transports,\n  from(transport) {\n    return `\"Awesome App\" \u003c${transport.options.from}\u003e`;\n  }\n});\n```\n\n### Using Redis for queue and scheduler\n\n*MailTime* uses separate storage for Queue management and Scheduler. In the example below Redis is used for both\n\n```js\nimport { MailTime, RedisQueue } from 'mail-time';\nimport { MongoClient } from 'mongodb';\nimport { createClient } from 'redis';\nimport { transports } from './transports.js';\n\nconst redisClient = await createClient({ url: 'redis://url' }).connect();\nconst mailQueue = new MailTime({\n  queue: new RedisQueue({\n    client: redisClient,\n  }),\n  josk: {\n    adapter: {\n      type: 'redis',\n      client: redisClient,\n    }\n  },\n  transports,\n  from(transport) {\n    return `\"Awesome App\" \u003c${transport.options.from}\u003e`;\n  }\n});\n```\n\n### Two `MailTime` instances usage example\n\nCreate two `MailTime` instances with different settings. One for urgent (*e.g. \"transactional\" emails*), and another one for other types of emails (*e.g. \"marketing\" emails*)\n\n```js\nimport { MailTime, RedisQueue } from 'mail-time';\nimport { createClient } from 'redis';\nimport { transports } from './transports.js';\nconst redisClient = await createClient({ url: 'redis://url' }).connect();\n\n// CREATE mailQueue FOR NON-URGENT EMAILS WHICH IS OKAY TO CONCATENATE\nconst mailQueue = new MailTime({\n  queue: new RedisQueue({\n    client: redisClient,\n  }),\n  transports,\n  strategy: 'backup',\n  failsToNext: 1,\n  concatEmails: true,\n  josk: {\n    adapter: {\n      type: 'redis',\n      client: redisClient\n    },\n    zombieTime: 120000\n  }\n});\n\n// CREATE mailInstantQueue FOR TRANSACTIONAL EMAILS AND ALERTS\nconst mailInstantQueue = new MailTime({\n  queue: new RedisQueue({\n    client: redisClient,\n    prefix: 'instant'\n  }),\n  transports,\n  prefix: 'instant',\n  retryDelay: 2000,\n  strategy: 'backup',\n  failsToNext: 1,\n  concatEmails: false,\n  josk: {\n    adapter: {\n      type: 'redis',\n      client: redisClient\n    },\n    zombieTime: 20000\n  }\n});\n\nawait mailQueue.sendMail({\n  to: 'user@gmail.com',\n  subject: 'You\\'ve got an email!',\n  text: 'Plain text message',\n  html: '\u003ch1\u003eHTML\u003c/h1\u003e\u003cp\u003eStyled message\u003c/p\u003e'\n});\n\nawait mailInstantQueue.sendMail({\n  to: 'user@gmail.com',\n  subject: 'Sign in request',\n  text: 'Your OTP login code: xxxx:',\n  html: '\u003ch1\u003eCode:\u003c/h1\u003e\u003ccode\u003eXXXX\u003c/code\u003e'\n});\n```\n\n### Passing variables to the template\n\nAll options passed to the `.sendMail()` method are available inside `text`, `html`, and global templates\n\n```js\nconst templates = {\n  global: '\u003chtml xmlns=\"http://www.w3.org/1999/xhtml\"\u003e\u003chead\u003e\u003ctitle\u003e{{subject}}\u003c/title\u003e\u003c/head\u003e\u003cbody\u003e{{{html}}}\u003cfooter\u003eMessage sent to @{{username}} user ({{to}})\u003c/footer\u003e\u003c/body\u003e\u003c/html\u003e',\n  signInCode: {\n    text: 'Hello @{{username}}! Here\\'s your login code: {{code}}',\n    html: `\u003ch1\u003eSign-in request\u003c/h1\u003e\u003cp\u003eHello @{{username}}! \u003cp\u003eCopy your login code below:\u003c/p\u003e \u003cpre\u003e\u003ccode\u003e{{code}}\u003c/code\u003e\u003c/pre\u003e`\n  }\n};\n\nconst mailQueue = new MailTime({\n  queue: new RedisQueue({ /* ... */ }),\n  template: templates.global\n});\n\nawait mailQueue.sendMail({\n  to: 'user@gmail.com',\n  subject: 'Sign-in request',\n  username: 'johndoe',\n  code: 'XXXXX-YY',\n  text: templates.signInCode.text,\n  html: templates.signInCode.html\n});\n```\n\n## API\n\nAll available constructor options and `.sendMail()` method API overview\n\n### `new MailTime(opts)` constructor\n\n- `opts` {*object*} - Configuration object\n- `opts.type` {*string*} - [Optional] `client` or `server`, default - `server`\n- `opts.queue` {*RedisQueue*|*MongoQueue*|*CustomQueue*} - Queue storage driver instance\n- `opts.transports` {*[object]*} - [*Required for \"server\"*] An array of `nodemailer`'s transports, returned from `nodemailer.createTransport({})`. Required for `{type: 'server'}`\n- `opts.josk` {*object*} - [*Required for \"server\"*] [`JoSk` package](https://github.com/veliovgroup/josk#api) options\n- `opts.josk.adapter` {*object*|*RedisAdapter*|*MongoAdapter*|*CustomAdapter*} - Config object or *Adapter* instance\n- `opts.josk.adapter.type` {*string*} - One of `mongo` *or* `redis`; Pass `josk.adapter.type` to avoid burden of creating *Adapter* instance manually\n- `opts.josk.adapter.client` {*RedisClient*} - *RedisClient* instance\n- `opts.josk.adapter.db` {*Db*} - Mongo's *Db* instance\n- `opts.josk[option]` {*mix*} - Any other options passed to [`JoSk` instance](https://github.com/veliovgroup/josk#api)\n- `opts.from` {*function*} - [Optional] A function which returns *string* of `from` field, format: `\"MyApp\" \u003cuser@example.com\u003e`\n- `opts.strategy` {*string*} - [Optional] `backup` or `balancer`, default - `backup`. If set to `backup`, first transport will be used unless failed to send `failsToNext` times. If set to `balancer` - transports will be used equally in round robin chain\n- `opts.failsToNext` {*number*} - [Optional] After how many failed \"send attempts\" switch to the next transport, applied only for `backup` strategy, default - `4`\n- `opts.prefix` {*string*} - [Optional] Use unique prefixes to create multiple `MailTime` instances within the same application\n- `opts.retries` {*number*} - [Optional] How many times resend failed emails, default - `60`\n- `opts.retryDelay` {*number*} - [Optional] Interval in *milliseconds* between send re-tries, default - `60000`\n- `opts.keepHistory` {*boolean*} - [Optional] By default sent emails not stored in the database. Set `{ keepHistory: true }` to keep queue task as it is in the database, default - `false`\n- `opts.concatEmails` {*boolean*} - [Optional] Concatenate email by `to` field (*e.g. to the same addressee*), default - `false`\n- `opts.concatSubject` {*string*} - [Optional] Email subject used in concatenated email, default - `Multiple notifications`\n- `opts.concatDelimiter` {*string*} - [Optional] HTML or plain string delimiter used between concatenated email, default - `\u003chr\u003e`\n- `opts.concatDelay` {*number*} - [Optional] Time in *milliseconds* while emails are waiting to be concatenated, default - `60000`\n- `opts.revolvingInterval` {*number*} - [Optional] Interval in *milliseconds* in between queue checks, default - `256`\n- `opts.template` {*string*} - [Optional] Mustache-like template, default - `{{{html}}}`, all options passed to `sendMail` is available in Template, like `to`, `subject`, `text`, `html` or any other custom option. Use `{{opt}}` for string placeholders and `{{{opt}}}` for html placeholders\n- `opts.onError(error, email, details)` {*function*} - [Optional] called when email has failed to get sent and exhausted all send attempts (`opts.retries`), called with 3 arguments:\n  - `error` {*Error*|*object*} - Error object\n  - `email` {*object*} - email's object\n  - `details` {*object*} - *not always present*, details from SMTP protocol\n- `opts.onSent(email, details)` {*function*} - [Optional] called when email was successfully handed over to receiving/recipient's SMTP server, called with 2 arguments:\n  - `email` {*object*} - email's object\n  - `details` {*object*} - *not always present*, details from SMTP server/protocol\n\n```js\nimport { MailTime, MongoQueue, RedisQueue } from 'mail-time';\nimport nodemailer from 'nodemailer';\nimport { createClient } from 'redis';\n\nconst redisClient = await createClient({ url: 'redis://url' }).connect();\n\nconst mailQueue = new MailTime({\n  type: 'server',\n  strategy: 'backup',\n  prefix: 'appMailQueue',\n  transports: [nodemailer.createTransport({/* ... */})],\n  failsToNext: 4,\n  retries: 60,\n  retryDelay: 60000,\n  keepHistory: false,\n  concatEmails: false,\n  concatDelay: 60000,\n  concatDelimiter: '\u003chr\u003e',\n  concatSubject: 'Multiple notifications',\n  revolvingInterval: 256,\n  template: '{{{html}}}',\n  queue: new RedisQueue({\n    client: redisClient,\n    prefix: 'appMailQueue',\n  }),\n  josk: {\n    adapter: {\n      type: 'redis',\n      client: redisClient,\n    }\n  },\n  from(transport) {\n    // To pass spam-filters `from` field should be correctly set\n    // for each transport, check `transport` object for more options\n    return `\"App Name\" \u003c${transport.options.from}\u003e`;\n  },\n  onError(error, email, details) {\n    console.log(`Email \"${email.mailOptions.subject}\" wasn't sent to ${email.mailOptions.to}`, error, details);\n  },\n  onSent(email, details) {\n    console.log(`Email \"${email.mailOptions.subject}\" successfully sent to ${email.mailOptions.to}`, details);\n  },\n});\n\nawait mailQueue.sendMail({\n  to: 'johndoe@example.com',\n  subject: 'Email subject',\n  text: 'You have got email!',\n  html: '\u003cp\u003eYou have got email!\u003c/p\u003e',\n});\n```\n\n### `new RedisQueue(opts)` constructor\n\n*Create Redis Queue instance.* Use for `opts.queue` when creating *MailTime* instance\n\n- `opts` {*object*} - Configuration object\n- `opts.client` {*RedisClient*} - Required, Redis'es `RedisClient` instance, like one returned from `await redis.createClient().connect()` method\n- `opts.prefix` {*string*} - Optional prefix for scope isolation; use when creating multiple `MailTime` instances within the single application\n\n```js\nimport { MailTime, RedisQueue } from 'mail-time';\nimport { createClient } from 'redis';\n\nnew RedisQueue({\n  client: await createClient({ url: 'redis://url' }).connect(),\n  prefix: 'appMailQueue',\n});\n```\n\n### `new MongoQueue(opts)` constructor\n\n*Create MongoDB Queue instance.* Use for `opts.queue` when creating *MailTime* instance\n\n- `opts` {*object*} - Configuration object\n- `opts.db` {*Db*} - Required, Mongo's `Db` instance, like one returned from `MongoClient#db()`\n- `opts.prefix` {*string*} - Optional prefix for scope isolation; use when creating multiple `MailTime` instances within the single application\n\n```js\nimport { MailTime, MongoQueue } from 'mail-time';\nimport { MongoClient } from 'mongodb';\n\nnew MongoQueue({\n  db: (await MongoClient.connect('mongodb://url')).db('database'),\n  prefix: 'appMailQueue',\n});\n```\n\n### `sendMail(opts)`\n\n*Add email to the queue.* Returns `Promise\u003cstring\u003e` unique email's `uuid`\n\n- `opts` {*object*} - Configuration object\n- `opts.sendAt` {*number*} - When email should be sent, default - `Date.now()`\n- `opts.template` {*string*} - Email specific template, this will override default template passed to `MailTime` constructor\n- `opts.concatSubject` {*string*} - Email specific concatenation subject, this will override default concatenation subject passed to `MailTime` constructor\n- `opts[key]` {*mix*} - Other custom and NodeMailer specific options, like `text`, `html` and `to`, [learn more here](https://nodemailer.com/message/). __Note:__ if [`attachments`](https://nodemailer.com/message/attachments/) are used via `path` — file must exists on all micro-services servers\n\n### `cancelMail(uuid)`\n\n*Remove email from queue.* Returns `Promise\u003cboolean\u003e` — `true` if cancelled or `false` if not found, was sent, or was cancelled previously. Throws *Error*\n\n- `uuid` {*string*|*promise*} — email's `uuid` returned from `.sendEmail()` method\n\n```js\nimport { mailQueue } from './mail-queue.js';\n\nconst uuid = await mailQueue.sendMail({\n  to: 'johndoe@example.com',\n  subject: 'Email subject',\n  text: 'You have got email!',\n  html: '\u003cp\u003eYou have got email!\u003c/p\u003e',\n});\n\nawait mailQueue.cancelMail(uuid);\n```\n\n### `ping()`\n\n*Ping MailTime instance, its scheduler, and its queue.* Returns `Promise\u003cobject\u003e`\n\n```js\nconst mailQueue = new MailTime({ /* ... */ });\n\nconst pingResult = await mailQueue.ping();\nconsole.log(pingResult)\n/**\nIn case of the successful response\n{\n  status: 'OK',\n  code: 200,\n  statusCode: 200,\n}\n\nFailed response\n{\n  status: 'Error reason',\n  code: 500,\n  statusCode: 500,\n  error: ErrorObject\n}\n*/\n```\n\n### `static MailTime.Template`\n\nSimple and bulletproof HTML template, see [its source](https://github.com/veliovgroup/mail-time/blob/master/template.html). Usage example:\n\n```js\nimport { MailTime, MongoQueue, RedisQueue } from 'mail-time';\n\n// Make it default\nconst mailQueue = new MailTime({\n  /* .. */\n  template: MailTime.Template\n});\n\n// For single letter\nmailQueue.sendMail({\n  /* .. */\n  template: MailTime.Template\n});\n```\n\n### Template Example\n\nPass custom template via `template` property to `.sendMail()` method\n\n```js\nmailQueue.sendMail({\n  to: 'user@gmail.com',\n  userName: 'Mike',\n  subject: 'Sign up confirmation',\n  text: 'Hello {{userName}}, \\r\\n Thank you for registration \\r\\n Your login: {{to}}',\n  html: '\u003cdiv style=\"text-align: center\"\u003e\u003ch1\u003eHello {{userName}}\u003c/h1\u003e\u003cp\u003e\u003cul\u003e\u003cli\u003eThank you for registration\u003c/li\u003e\u003cli\u003eYour login: {{to}}\u003c/li\u003e\u003c/ul\u003e\u003c/p\u003e\u003c/div\u003e',\n  template: '\u003cbody\u003e{{{html}}}\u003c/body\u003e'\n});\n```\n\n## Testing\n\n1. Clone this package\n2. Start local or obtain URLs for remote MongoDB and Redis servers\n3. In Terminal (*Console*) go to directory where package was cloned\n4. Then run:\n\n```shell\n# Before running tests make sure NODE_ENV === development\n# Install NPM dependencies\nnpm install --save-dev\n\n# DEFAULT RUN\nREDIS_URL=\"redis://127.0.0.1:6379\" MONGO_URL=\"mongodb://127.0.0.1:27017/npm-mail-time-test-001\" npm test\n\n# OPTIONALLY RUN WITH CUSTOM DOMAIN\nEMAIL_DOMAIN=\"your-domain.com\" REDIS_URL=\"redis://127.0.0.1:6379\" MONGO_URL=\"mongodb://127.0.0.1:27017/npm-mail-time-test-001\" npm test\n\n# IF SOME TESTS ARE FAILING: ENABLE DEBUG\nDEBUG=\"true\" REDIS_URL=\"redis://127.0.0.1:6379\" MONGO_URL=\"mongodb://127.0.0.1:27017/npm-mail-time-test-001\" npm test\n\n# Be patient, tests are taking around 8 mins\n```\n\n## Support this project:\n\n- Upload and share files using [☄️ meteor-files.com](https://meteor-files.com/?ref=github-mail-time-repo-footer) — Continue interrupted file uploads without losing any progress. There is nothing that will stop Meteor from delivering your file to the desired destination\n- Use [▲ ostr.io](https://ostr.io?ref=github-mail-time-repo-footer) for [Server Monitoring](https://snmp-monitoring.com), [Web Analytics](https://ostr.io/info/web-analytics?ref=github-mail-time-repo-footer), [WebSec](https://domain-protection.info), [Web-CRON](https://web-cron.info) and [SEO Pre-rendering](https://prerendering.com) of a website\n- Star on [GitHub](https://github.com/veliovgroup/mail-time)\n- Star on [NPM](https://www.npmjs.com/package/mail-time)\n- Star on [Atmosphere](https://atmospherejs.com/ostrio/mailer)\n- [Sponsor maintainer via GitHub](https://github.com/sponsors/dr-dimitru) — support open source with one-time contribution or on a regular basis\n- [Sponsor veliovgroup via GitHub](https://github.com/sponsors/veliovgroup) — support company behind this package\n- [Support via PayPal](https://paypal.me/veliovgroup) — support our open source contributions\n","funding_links":["https://github.com/sponsors/dr-dimitru","https://paypal.me/veliovgroup","https://github.com/sponsors/veliovgroup"],"categories":["server"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVeliovGroup%2FMail-Time","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FVeliovGroup%2FMail-Time","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVeliovGroup%2FMail-Time/lists"}