summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/util/config/types/EmailConfiguration.ts2
-rw-r--r--src/util/config/types/subconfigurations/email/MailJet.ts22
-rw-r--r--src/util/config/types/subconfigurations/email/index.ts1
-rw-r--r--src/util/util/Email.ts163
4 files changed, 133 insertions, 55 deletions
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 <https://www.gnu.org/licenses/>.
+*/
+
+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,45 +52,20 @@ export function adjustEmail(email?: string): string | undefined {
 	// return email;
 }
 
-export const Email: {
-	transporter: Transporter | null;
-	init: () => Promise<void>;
-	initSMTP: () => Promise<void>;
-	initMailgun: () => Promise<void>;
-	generateVerificationLink: (id: string, email: string) => Promise<string>;
-	sendVerificationEmail: (user: User, email: string) => Promise<any>;
-	doReplacements: (
-		template: string,
-		user: User,
-		emailVerificationUrl?: string,
-		passwordResetUrl?: string,
-		ipInfo?: {
-			ip: string;
-			city: string;
-			region: string;
-			country_name: string;
-		},
-	) => string;
-} = {
-	transporter: null,
-	init: async function () {
-		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 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.",
 			);
 
-		console.log(`[Email] Initializing SMTP transport: ${host}`);
-		this.transporter = nodemailer.createTransport({
+		// construct the transporter
+		const transporter = nodemailer.createTransport({
 			host,
 			port,
 			secure,
@@ -100,41 +75,117 @@ export const Email: {
 			},
 		});
 
-		await this.transporter.verify((error, _) => {
-			if (error) {
-				console.error(`[Email] SMTP error: ${error}`);
-				this.transporter?.close();
-				this.transporter = null;
-				return;
-			}
-			console.log(`[Email] Ready`);
+		// 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;
 	},
-	initMailgun: async function () {
+	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 {
-			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`);
+			// 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] Mailgun transport is not installed. Please run `npm install nodemailer-mailgun-transport --save` to install it.",
+				"[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<void>;
+	generateVerificationLink: (id: string, email: string) => Promise<string>;
+	sendVerificationEmail: (user: User, email: string) => Promise<any>;
+	doReplacements: (
+		template: string,
+		user: User,
+		emailVerificationUrl?: string,
+		passwordResetUrl?: string,
+		ipInfo?: {
+			ip: string;
+			city: string;
+			region: string;
+			country_name: string;
+		},
+	) => string;
+} = {
+	transporter: null,
+	init: async function () {
+		const { provider } = Config.get().email;
+		if (!provider) 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>(.*)<\/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);
 	},
 };