summary refs log tree commit diff
path: root/src/util/util/Email.ts
diff options
context:
space:
mode:
authorPuyodead1 <puyodead@proton.me>2023-01-21 13:53:26 -0500
committerPuyodead1 <puyodead@protonmail.com>2023-02-23 22:38:02 -0500
commitbf55ebc81fa8d3cc4aa4e6fd3735ff0ee659505a (patch)
treeaaa4f9ed991139f03c71074e04aba7e79e428b55 /src/util/util/Email.ts
parentAdd Mailgun transport (diff)
downloadserver-bf55ebc81fa8d3cc4aa4e6fd3735ff0ee659505a.tar.xz
Add mailjet transport
Diffstat (limited to '')
-rw-r--r--src/util/util/Email.ts163
1 files changed, 108 insertions, 55 deletions
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);
 	},
 };