Add mailjet transport

This commit is contained in:
Puyodead1 2023-01-21 13:53:26 -05:00 committed by Puyodead1
parent 4383fcd449
commit bf55ebc81f
6 changed files with 142 additions and 63 deletions

BIN
package-lock.json generated

Binary file not shown.

View File

@ -116,6 +116,7 @@
"optionalDependencies": { "optionalDependencies": {
"erlpack": "^0.1.4", "erlpack": "^0.1.4",
"sqlite3": "^5.1.4", "sqlite3": "^5.1.4",
"nodemailer-mailgun-transport": "^2.1.5" "nodemailer-mailgun-transport": "^2.1.5",
"nodemailer-mailjet-transport": "^1.0.4"
} }
} }

View File

@ -18,6 +18,7 @@
import { import {
MailGunConfiguration, MailGunConfiguration,
MailJetConfiguration,
SMTPConfiguration, SMTPConfiguration,
} from "./subconfigurations/email"; } from "./subconfigurations/email";
@ -25,4 +26,5 @@ export class EmailConfiguration {
provider: string | null = null; provider: string | null = null;
smtp: SMTPConfiguration = new SMTPConfiguration(); smtp: SMTPConfiguration = new SMTPConfiguration();
mailgun: MailGunConfiguration = new MailGunConfiguration(); mailgun: MailGunConfiguration = new MailGunConfiguration();
mailjet: MailJetConfiguration = new MailJetConfiguration();
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
export class MailJetConfiguration {
apiKey: string | null = null;
apiSecret: string | null = null;
}

View File

@ -17,4 +17,5 @@
*/ */
export * from "./MailGun"; export * from "./MailGun";
export * from "./MailJet";
export * from "./SMTP"; export * from "./SMTP";

View File

@ -52,11 +52,111 @@ export function adjustEmail(email?: string): string | undefined {
// return email; // 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: { export const Email: {
transporter: Transporter | null; transporter: Transporter | null;
init: () => Promise<void>; init: () => Promise<void>;
initSMTP: () => Promise<void>;
initMailgun: () => Promise<void>;
generateVerificationLink: (id: string, email: string) => Promise<string>; generateVerificationLink: (id: string, email: string) => Promise<string>;
sendVerificationEmail: (user: User, email: string) => Promise<any>; sendVerificationEmail: (user: User, email: string) => Promise<any>;
doReplacements: ( doReplacements: (
@ -77,64 +177,15 @@ export const Email: {
const { provider } = Config.get().email; const { provider } = Config.get().email;
if (!provider) return; if (!provider) return;
if (provider === "smtp") await this.initSMTP(); const transporterFn =
else if (provider === "mailgun") await this.initMailgun(); transporters[provider as keyof typeof transporters];
else throw new Error(`Unknown email provider: ${provider}`); if (!transporterFn)
}, return console.error(`[Email] Invalid provider: ${provider}`);
initSMTP: async function () { console.log(`[Email] Initializing ${provider} transport...`);
const { host, port, secure, username, password } = const transporter = await transporterFn();
Config.get().email.smtp; if (!transporter) return;
if (!host || !port || !secure || !username || !password) this.transporter = transporter;
return console.error( console.log(`[Email] ${provider} transport initialized.`);
"[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;
}
}, },
/** /**
* Replaces all placeholders in an email template with the correct values * Replaces all placeholders in an email template with the correct values
@ -214,6 +265,7 @@ export const Email: {
user.id, user.id,
email, email,
); );
// load the email template // load the email template
const rawTemplate = fs.readFileSync( const rawTemplate = fs.readFileSync(
path.join( path.join(
@ -223,13 +275,14 @@ export const Email: {
), ),
{ encoding: "utf-8" }, { encoding: "utf-8" },
); );
// replace email template placeholders // replace email template placeholders
const html = this.doReplacements(rawTemplate, user, verificationLink); const html = this.doReplacements(rawTemplate, user, verificationLink);
// extract the title from the email template to use as the email subject // extract the title from the email template to use as the email subject
const subject = html.match(/<title>(.*)<\/title>/)?.[1] || ""; const subject = html.match(/<title>(.*)<\/title>/)?.[1] || "";
// // construct the email // construct the email
const message = { const message = {
from: from:
Config.get().general.correspondenceEmail || "noreply@localhost", Config.get().general.correspondenceEmail || "noreply@localhost",
@ -238,7 +291,7 @@ export const Email: {
html, html,
}; };
// // send the email // send the email
return this.transporter.sendMail(message); return this.transporter.sendMail(message);
}, },
}; };