From bf55ebc81fa8d3cc4aa4e6fd3735ff0ee659505a Mon Sep 17 00:00:00 2001 From: Puyodead1 Date: Sat, 21 Jan 2023 13:53:26 -0500 Subject: [PATCH] Add mailjet transport --- package-lock.json | 267 ++++++++++++++++++ package.json | 3 +- src/util/config/types/EmailConfiguration.ts | 2 + .../types/subconfigurations/email/MailJet.ts | 22 ++ .../types/subconfigurations/email/index.ts | 1 + src/util/util/Email.ts | 177 ++++++++---- 6 files changed, 409 insertions(+), 63 deletions(-) create mode 100644 src/util/config/types/subconfigurations/email/MailJet.ts diff --git a/package-lock.json b/package-lock.json index a07a7ec7..c6960e6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,6 +82,7 @@ "optionalDependencies": { "erlpack": "^0.1.4", "nodemailer-mailgun-transport": "^2.1.5", + "nodemailer-mailjet-transport": "^1.0.4", "sqlite3": "^5.1.4" } }, @@ -2384,6 +2385,12 @@ "node": ">=0.4.0" } }, + "node_modules/addressparser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", + "integrity": "sha512-aQX7AISOMM7HFE0iZ3+YnD07oIeJqWGVnJ+ZIKaBZAk03ftmVYVqsGas/rbXKR21n4D/hKCSHypvcyOkds/xzg==", + "optional": true + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -2618,6 +2625,12 @@ "node": ">=8" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "optional": true + }, "node_modules/asn1js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", @@ -2642,6 +2655,12 @@ "node": ">=4" } }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "optional": true + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3176,6 +3195,12 @@ "node": ">= 0.8" } }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "optional": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3310,6 +3335,12 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "optional": true + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -3453,6 +3484,16 @@ "node": ">=8" } }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "optional": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -3579,6 +3620,15 @@ "iconv-lite": "^0.6.2" } }, + "node_modules/encoding-japanese": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.0.0.tgz", + "integrity": "sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==", + "optional": true, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/encoding/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -4224,6 +4274,12 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "optional": true + }, "node_modules/fast-xml-parser": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", @@ -4417,6 +4473,21 @@ "node": ">= 6" } }, + "node_modules/formidable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.1.tgz", + "integrity": "sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ==", + "optional": true, + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -4670,6 +4741,15 @@ "node": ">=10.0.0" } }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -5176,6 +5256,42 @@ "node": ">= 0.8.0" } }, + "node_modules/libbase64": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.2.1.tgz", + "integrity": "sha512-l+nePcPbIG1fNlqMzrh68MLkX/gTxk/+vdvAb388Ssi7UuUN31MI44w4Yf33mM3Cm4xDfw48mdf3rkdHszLNew==", + "optional": true + }, + "node_modules/libmime": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.2.1.tgz", + "integrity": "sha512-A0z9O4+5q+ZTj7QwNe/Juy1KARNb4WaviO4mYeFC4b8dBT2EEqK2pkM+GC8MVnkOjqhl5nYQxRgnPYRRTNmuSQ==", + "optional": true, + "dependencies": { + "encoding-japanese": "2.0.0", + "iconv-lite": "0.6.3", + "libbase64": "1.2.1", + "libqp": "2.0.1" + } + }, + "node_modules/libmime/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/libqp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.0.1.tgz", + "integrity": "sha512-Ka0eC5LkF3IPNQHJmYBWljJsw0UvM6j+QdKRbWyCdTmYwvIDE6a7bCm0UkTAL/K+3KXK5qXT/ClcInU01OpdLg==", + "optional": true + }, "node_modules/lie": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", @@ -5895,6 +6011,18 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/node-mailjet": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/node-mailjet/-/node-mailjet-3.4.1.tgz", + "integrity": "sha512-m+msgBJYgwFbIZBIPOnsGOtBt9xP03UqmkmuEcgTcLlr/U1GUJQrVI7cDFRgujybb9Cl1wl4thIGyM3wt6X+zQ==", + "optional": true, + "dependencies": { + "json-bigint": "^1.0.0", + "qs": "^6.5.0", + "superagent": "^7.1.1", + "superagent-proxy": "^3.0.0" + } + }, "node_modules/node-os-utils": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/node-os-utils/-/node-os-utils-1.3.7.tgz", @@ -5908,6 +6036,26 @@ "node": ">=6.0.0" } }, + "node_modules/nodemailer-build-attachment": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/nodemailer-build-attachment/-/nodemailer-build-attachment-3.0.0.tgz", + "integrity": "sha512-8hoic5t/tpNMfrRoHW7rwpEpjrp1ZMSYloBZHhCZHnin+Htxr+egR4ufrFeHC0ueSFjmsvMDr5veaQ4KpYvTNA==", + "optional": true, + "dependencies": { + "libbase64": "^1.2.1", + "libmime": "^5.0.0", + "nodemailer-fetch": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nodemailer-fetch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-2.1.0.tgz", + "integrity": "sha512-XwPvtBfUgIHhrJora9wIRbI4fvx8iYpSE2iItpM3e+SnsVRKm+9UeMfKQbk8I1WcOaT370E8oaLJE/vN15/ggQ==", + "optional": true + }, "node_modules/nodemailer-mailgun-transport": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/nodemailer-mailgun-transport/-/nodemailer-mailgun-transport-2.1.5.tgz", @@ -5919,6 +6067,32 @@ "mailgun.js": "^8.0.1" } }, + "node_modules/nodemailer-mailjet-transport": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nodemailer-mailjet-transport/-/nodemailer-mailjet-transport-1.0.4.tgz", + "integrity": "sha512-0fw7y75390IGjjIAePQq9d6uARLQceV4OiR7Z5QO0gOCXWlzjgJdvzl8k++Na+7aS7QPNr5fZkpvtF+IuNppow==", + "optional": true, + "dependencies": { + "addressparser": "^1.0.1", + "async": "^3.2.0", + "bluebird": "^3.7.2", + "dotenv": "^10.0.0", + "node-mailjet": "^3.3.4", + "nodemailer-build-attachment": "^3.0.0" + }, + "peerDependencies": { + "nodemailer": ">=4.x" + } + }, + "node_modules/nodemailer-mailjet-transport/node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "optional": true, + "engines": { + "node": ">=10" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -7039,6 +7213,99 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/superagent": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.5.tgz", + "integrity": "sha512-HQYyGuDRFGmZ6GNC4hq2f37KnsY9Lr0/R1marNZTgMweVDQLTLJJ6DGQ9Tj/xVVs5HEnop9EMmTbywb5P30aqw==", + "optional": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.3", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.0.1", + "methods": "^1.1.2", + "mime": "^2.5.0", + "qs": "^6.10.3", + "readable-stream": "^3.6.0", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" + } + }, + "node_modules/superagent-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/superagent-proxy/-/superagent-proxy-3.0.0.tgz", + "integrity": "sha512-wAlRInOeDFyd9pyonrkJspdRAxdLrcsZ6aSnS+8+nu4x1aXbz6FWSTT9M6Ibze+eG60szlL7JA8wEIV7bPWuyQ==", + "optional": true, + "dependencies": { + "debug": "^4.3.2", + "proxy-agent": "^5.0.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "superagent": ">= 0.15.4 || 1 || 2 || 3" + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/readable-stream": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/superagent/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/superagent/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", diff --git a/package.json b/package.json index 8a6c0405..819b040a 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "optionalDependencies": { "erlpack": "^0.1.4", "sqlite3": "^5.1.4", - "nodemailer-mailgun-transport": "^2.1.5" + "nodemailer-mailgun-transport": "^2.1.5", + "nodemailer-mailjet-transport": "^1.0.4" } } diff --git a/src/util/config/types/EmailConfiguration.ts b/src/util/config/types/EmailConfiguration.ts index 34550f4c..625507f2 100644 --- a/src/util/config/types/EmailConfiguration.ts +++ b/src/util/config/types/EmailConfiguration.ts @@ -18,6 +18,7 @@ import { MailGunConfiguration, + MailJetConfiguration, SMTPConfiguration, } from "./subconfigurations/email"; @@ -25,4 +26,5 @@ export class EmailConfiguration { provider: string | null = null; smtp: SMTPConfiguration = new SMTPConfiguration(); mailgun: MailGunConfiguration = new MailGunConfiguration(); + mailjet: MailJetConfiguration = new MailJetConfiguration(); } diff --git a/src/util/config/types/subconfigurations/email/MailJet.ts b/src/util/config/types/subconfigurations/email/MailJet.ts new file mode 100644 index 00000000..eccda8ac --- /dev/null +++ b/src/util/config/types/subconfigurations/email/MailJet.ts @@ -0,0 +1,22 @@ +/* + Fosscord: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Fosscord and Fosscord Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +export class MailJetConfiguration { + apiKey: string | null = null; + apiSecret: string | null = null; +} diff --git a/src/util/config/types/subconfigurations/email/index.ts b/src/util/config/types/subconfigurations/email/index.ts index 92fe9184..02cc564c 100644 --- a/src/util/config/types/subconfigurations/email/index.ts +++ b/src/util/config/types/subconfigurations/email/index.ts @@ -17,4 +17,5 @@ */ export * from "./MailGun"; +export * from "./MailJet"; export * from "./SMTP"; diff --git a/src/util/util/Email.ts b/src/util/util/Email.ts index b8019cbd..8899b3c2 100644 --- a/src/util/util/Email.ts +++ b/src/util/util/Email.ts @@ -52,11 +52,111 @@ export function adjustEmail(email?: string): string | undefined { // return email; } +const transporters = { + smtp: async function () { + // get configuration + const { host, port, secure, username, password } = + Config.get().email.smtp; + + // ensure all required configuration values are set + if (!host || !port || !secure || !username || !password) + return console.error( + "[Email] SMTP has not been configured correctly.", + ); + + // construct the transporter + const transporter = nodemailer.createTransport({ + host, + port, + secure, + auth: { + user: username, + pass: password, + }, + }); + + // verify connection configuration + const verified = await transporter.verify().catch((err) => { + console.error("[Email] SMTP verification failed:", err); + return; + }); + + // if verification failed, return void and don't set transporter + if (!verified) return; + + return transporter; + }, + mailgun: async function () { + // get configuration + const { apiKey, domain } = Config.get().email.mailgun; + + // ensure all required configuration values are set + if (!apiKey || !domain) + return console.error( + "[Email] Mailgun has not been configured correctly.", + ); + + let mg; + try { + // try to import the transporter package + mg = require("nodemailer-mailgun-transport"); + } catch { + // if the package is not installed, log an error and return void so we don't set the transporter + console.error( + "[Email] Mailgun transport is not installed. Please run `npm install nodemailer-mailgun-transport --save-optional` to install it.", + ); + return; + } + + // create the transporter configuration object + const auth = { + auth: { + api_key: apiKey, + domain: domain, + }, + }; + + // create the transporter and return it + return nodemailer.createTransport(mg(auth)); + }, + mailjet: async function () { + // get configuration + const { apiKey, apiSecret } = Config.get().email.mailjet; + + // ensure all required configuration values are set + if (!apiKey || !apiSecret) + return console.error( + "[Email] Mailjet has not been configured correctly.", + ); + + let mj; + try { + // try to import the transporter package + mj = require("nodemailer-mailjet-transport"); + } catch { + // if the package is not installed, log an error and return void so we don't set the transporter + console.error( + "[Email] Mailjet transport is not installed. Please run `npm install nodemailer-mailjet-transport --save-optional` to install it.", + ); + return; + } + + // create the transporter configuration object + const auth = { + auth: { + apiKey: apiKey, + apiSecret: apiSecret, + }, + }; + + // create the transporter and return it + return nodemailer.createTransport(mj(auth)); + }, +}; + export const Email: { transporter: Transporter | null; init: () => Promise; - initSMTP: () => Promise; - initMailgun: () => Promise; generateVerificationLink: (id: string, email: string) => Promise; sendVerificationEmail: (user: User, email: string) => Promise; doReplacements: ( @@ -77,64 +177,15 @@ export const Email: { const { provider } = Config.get().email; if (!provider) return; - if (provider === "smtp") await this.initSMTP(); - else if (provider === "mailgun") await this.initMailgun(); - else throw new Error(`Unknown email provider: ${provider}`); - }, - initSMTP: async function () { - const { host, port, secure, username, password } = - Config.get().email.smtp; - if (!host || !port || !secure || !username || !password) - return console.error( - "[Email] SMTP has not been configured correctly.", - ); - - console.log(`[Email] Initializing SMTP transport: ${host}`); - this.transporter = nodemailer.createTransport({ - host, - port, - secure, - auth: { - user: username, - pass: password, - }, - }); - - await this.transporter.verify((error, _) => { - if (error) { - console.error(`[Email] SMTP error: ${error}`); - this.transporter?.close(); - this.transporter = null; - return; - } - console.log(`[Email] Ready`); - }); - }, - initMailgun: async function () { - const { apiKey, domain } = Config.get().email.mailgun; - if (!apiKey || !domain) - return console.error( - "[Email] Mailgun has not been configured correctly.", - ); - - try { - const mg = require("nodemailer-mailgun-transport"); - const auth = { - auth: { - api_key: apiKey, - domain: domain, - }, - }; - - console.log(`[Email] Initializing Mailgun transport...`); - this.transporter = nodemailer.createTransport(mg(auth)); - console.log(`[Email] Ready`); - } catch { - console.error( - "[Email] Mailgun transport is not installed. Please run `npm install nodemailer-mailgun-transport --save` to install it.", - ); - return; - } + const transporterFn = + transporters[provider as keyof typeof transporters]; + if (!transporterFn) + return console.error(`[Email] Invalid provider: ${provider}`); + console.log(`[Email] Initializing ${provider} transport...`); + const transporter = await transporterFn(); + if (!transporter) return; + this.transporter = transporter; + console.log(`[Email] ${provider} transport initialized.`); }, /** * Replaces all placeholders in an email template with the correct values @@ -214,6 +265,7 @@ export const Email: { user.id, email, ); + // load the email template const rawTemplate = fs.readFileSync( path.join( @@ -223,13 +275,14 @@ export const Email: { ), { encoding: "utf-8" }, ); + // replace email template placeholders const html = this.doReplacements(rawTemplate, user, verificationLink); // extract the title from the email template to use as the email subject const subject = html.match(/(.*)<\/title>/)?.[1] || ""; - // // construct the email + // construct the email const message = { from: Config.get().general.correspondenceEmail || "noreply@localhost", @@ -238,7 +291,7 @@ export const Email: { html, }; - // // send the email + // send the email return this.transporter.sendMail(message); }, };