diff --git a/package-lock.json b/package-lock.json
index a07a7ec7..c6960e6c 100644
Binary files a/package-lock.json and b/package-lock.json differ
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);
},
};