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 | Bin 276467 -> 285969 bytes 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, 142 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 a07a7ec7553991efe4baebfe83eb6df91b947ec7..c6960e6c80bc2696a7d1e8398e36f8a38cd5ffaf 100644 GIT binary patch delta 4236 zcmZu!TdW($dB&1Q5+pTpR3k^0WL4(^$wAiS?sBe}4SUf4Kba+vjgvwyK$uG1lDJx*2MI%y%Wrb8Oe& zdtqnq6)cX%ldr#Z{@|_i(3hHLRn^rz58S&BJqzCbRTR4}9ai zS;;&yU~w48|i+JJHb6jx%*|PivJs%|l8RzXHjI)wVH-S@`#(q zO*Zh$Q_;;)gGwOdZUznNGTbaQvq1snyLL8%c2!;Sy~c5-u7GcTA3A$*>3h&0J$=?5 z8QM@PuiJZMWl-3wh?860aECKF+7upxA`8jEqT?Xa`%PLb5Gee@8 zLb~{{hL~b`nn&^)jkS5RVv$6`;BrJaDP)(q%orxv8rvB!1c8E^W=rHuw9Jb$Cr?x+ z;K3IlI_6(CvmJ7aV(GFMcrg==vEtxUz=4-lY%Wm4Lu&meUw{weHW1=;){L*ZT zwMl9@ZH39gqU6O8t{cK7;wX6g?;_a2&4*C?^10YhawJOw)Ck&M?OeidX_jKEhSfis z@U+m!kJwiCwr`0r%%P2eCX2artwPNzYzT5(6=1?v2%Do#2P+41&!K``j~EG6YY+t4 z0aG3(h82xPO2Z6ps9d&f(gC00+ck}FJGc{q?k0HeYmw_v8a%ig**mZ!k*_zSo}_~h znvwn0&OaltE_r?|v}iHUcSBbVeGxkOJFH=TC-X>uJd8C|VGUH5D zp~{rb`5c+;rjtRd+TqESI=8S2mlDvBJx7#7$@Tc+@R*=7Z98r=$I z)I!wx|I9J(d)#cB)nr8dv@ z{3+d_DK(oLXr5{Jd#)dJrmePDEcb*_qA9}bi(=ACOly{1?WgDh28$z%tYaC9GZZWv z6w`7ptt3oh4e>+RfcqDr8wag7B18d_SChZJ23~tTieDKUvMhz=CmDKk+>c@4;VU8i z$FQYw3MbPgOs|h8wWT{C)3xfTgY<>@2!W^Sl#=muy6G7yF%auGR-=>2S+c9+xLLz< zW^$g_XUVjw>bN^n%igkD*EaOu3imPr^~LBF@P!{k_|xm5VXjee)Qb^^E>P$)!zN*3 z0{4Vril5arq!KJJhHBL1WLugVY|`v%#avQOt63pYC`_1yGpfO*kb5m_#11EeF=2K2 zd82P}-9|&Gn?Z98iOV11F8G^2jqW039Djs?<9Mu-j>Qvk?9|L;Q#BG`_d#U$?4kDu z(@5mcp!Lxvn`?1(|JTUJ)}!^cxM_j&oiNjkglajWIx6BcSLe$Wvzu*?hZS#ziqjf7 zgn5-|$n8Z=70m8zO6Lotl#+8MDvkuIiZC$FbQogN5s=(ip)C=`SVyfM7yRvm&%Gb{ zp>p<6R;>7c`2>Wo9{xOWV=XxziH*>q)593PdifXoq6wQ`KU?YcXGNLrWoeGJyb|kY z{mh(0adBdf%%Gt31*JN65fLki72a*pDJ5TQ^n>9-mNU6AHXA4X0bN?u@R?6`;N?2l z0R2v6Yc-2refgL{#0jgsz|mZ(A6}swcBSIjNu*EaVvcLi7XU$_+u)t&qh}5}=_np~ zN)C*%8k2lqQU<1GtNyh0;g*y0*3=x&b;1%k<$MLnM!zLVIpiV+{>O8vUD}&971h^NE;DB31w!t62 z5WNU?Zbz?!_pU{-{U_Hye(23Bz(k1L5<4?)>SuMaPnQR7#df94jBdzuQlHM#M%QT; z5(e!%HC%(6me*=9CY%wqn$Ga@C>=@wg_Q(6g%;AbRIL<{X&obwem_k8kx&ZV`Wm$T zyCpC9%0EV*S$*+C{B*WHB8ngP{3FwcG6B_RsYQywGbmje4rxl`_(?`31FBKw6|I$I zjYg_R@GRPAbDj2hIqX-|>2jPN!EjxIQ&KaD$Z2jKy%3Kl z_Z~fWDyvW87I0h9ZQwi?y|((RtxIR_-bu#iE3EVE8W>Mqdh9a;0e|@+^hpR^?OsZN z|NaoV455dZcgI;Hqo#+>G}F|v(U=?7^&%Ct^FyXO6X;3>Y0Z6G2mwM4tlnHHuzar9 zPuO%7ueGzGm@YYDo?#1CJ)f#}1etbt6c4d*bP>IL5Z;O8OVJPBifHcY;Tu<>6u9?7 zG;#HW>tV7_2C)>_c`>>#mig|Y6yzJllG!6m^;~i=N$7ZrvAk}k>Y_*kGbOj)6Z*B8 z*2?o$p<1bs`4XQIhRD3t@`!T5vYkw!W(x9RCKDWvN*ghH6}~|w&jj0o})IXxmtABMMIY2MQ;IZU_WGo(ri9w|+Foh$`+&ZBt&=o3wIi+hb=W z#}u$~zH5XvO64?0L+1H%eMwWamCR+ZE>GH;RT${q$wa}@X?#+ahnvX$$LFKqlmiE! a`7yK=0q>Df__XyQ8I_@f>pzPARQrGLRDbRO delta 117 zcmV-*0E+*Sx)Jm55U`yIgUboG%LxIBQ@3wk0mC1+F@FKs0=Eo<0Y*xfFVO)7w|&k5 zeiOF|;{lWtx9Il)RuQ+63IeiIw{KqpUKqEbc>>WOx1XB=G)1=u<^s)Qw{%MbGXj%( X2^6=xPy?9(xBOKD=9RZR_XE{sGxso{ 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); }, };