import { SMTPServer, SMTPServerOptions, SMTPServerSession } from "smtp-server"; import { Readable } from "stream"; import dotenv from "dotenv"; import { simpleParser } from "mailparser"; import { readFileSync, watch, FSWatcher } from "fs"; dotenv.config(); const AUTH_USERNAME = process.env.SMTP_AUTH_USERNAME ?? "usesend"; const BASE_URL = process.env.USESEND_BASE_URL ?? process.env.UNSEND_BASE_URL ?? "https://app.usesend.com"; const SSL_KEY_PATH = process.env.USESEND_API_KEY_PATH ?? process.env.UNSEND_API_KEY_PATH; const SSL_CERT_PATH = process.env.USESEND_API_CERT_PATH ?? process.env.UNSEND_API_CERT_PATH; async function sendEmailToUseSend(emailData: any, apiKey: string) { try { const apiEndpoint = "/api/v1/emails"; const url = new URL(apiEndpoint, BASE_URL); // Combine base URL with endpoint console.log("Sending email to useSend API at:", url.href); // Debug statement const emailDataText = JSON.stringify(emailData); const response = await fetch(url.href, { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: emailDataText, }); if (!response.ok) { const errorData = await response.text(); console.error( "useSend API error response: error:", JSON.stringify(errorData, null, 4), `\nemail data: ${emailDataText}`, ); throw new Error( `Failed to send email: ${errorData || "Unknown error from server"}`, ); } const responseData = await response.json(); console.log("useSend API response:", responseData); } catch (error) { if (error instanceof Error) { console.error("Error message:", error.message); throw new Error(`Failed to send email: ${error.message}`); } else { console.error("Unexpected error:", error); throw new Error("Failed to send email: Unexpected error occurred"); } } } function loadCertificates(): { key?: Buffer; cert?: Buffer } { return { key: SSL_KEY_PATH ? readFileSync(SSL_KEY_PATH) : undefined, cert: SSL_CERT_PATH ? readFileSync(SSL_CERT_PATH) : undefined, }; } const initialCerts = loadCertificates(); const serverOptions: SMTPServerOptions = { secure: false, key: initialCerts.key, cert: initialCerts.cert, onData( stream: Readable, session: SMTPServerSession, callback: (error?: Error) => void, ) { console.log("Receiving email data..."); // Debug statement simpleParser(stream, (err, parsed) => { if (err) { console.error("Failed to parse email data:", err.message); return callback(err); } if (!session.user) { console.error("No API key found in session"); return callback(new Error("No API key found in session")); } const emailObject = { to: Array.isArray(parsed.to) ? parsed.to.map((addr) => addr.text).join(", ") : parsed.to?.text, from: Array.isArray(parsed.from) ? parsed.from.map((addr) => addr.text).join(", ") : parsed.from?.text, subject: parsed.subject, text: parsed.text, html: parsed.html, replyTo: parsed.replyTo?.text, }; sendEmailToUseSend(emailObject, session.user) .then(() => callback()) .then(() => console.log("Email sent successfully to: ", emailObject.to)) .catch((error) => { console.error("Failed to send email:", error.message); callback(error); }); }); }, onAuth(auth, session: any, callback: (error?: Error, user?: any) => void) { if (auth.username === AUTH_USERNAME && auth.password) { console.log("Authenticated successfully"); // Debug statement callback(undefined, { user: auth.password }); } else { console.error("Invalid username or password"); callback(new Error("Invalid username or password")); } }, size: 10485760, }; function startServers() { const servers: SMTPServer[] = []; const watchers: FSWatcher[] = []; if (SSL_KEY_PATH && SSL_CERT_PATH) { // Implicit SSL/TLS for ports 465 and 2465 [465, 2465].forEach((port) => { const server = new SMTPServer({ ...serverOptions, secure: true }); server.listen(port, () => { console.log( `Implicit SSL/TLS SMTP server is listening on port ${port}`, ); }); server.on("error", (err) => { console.error(`Error occurred on port ${port}:`, err); }); servers.push(server); }); } // STARTTLS for ports 25, 587, and 2587 [25, 587, 2587].forEach((port) => { const server = new SMTPServer(serverOptions); server.listen(port, () => { console.log(`STARTTLS SMTP server is listening on port ${port}`); }); server.on("error", (err) => { console.error(`Error occurred on port ${port}:`, err); }); servers.push(server); }); if (SSL_KEY_PATH && SSL_CERT_PATH) { const reloadCertificates = () => { try { const { key, cert } = loadCertificates(); if (key && cert) { servers.forEach((srv) => srv.updateSecureContext({ key, cert })); console.log("TLS certificates reloaded"); } } catch (err) { console.error("Failed to reload TLS certificates", err); } }; [SSL_KEY_PATH, SSL_CERT_PATH].forEach((file) => { watchers.push(watch(file, { persistent: false }, reloadCertificates)); }); } return { servers, watchers }; } const { servers, watchers } = startServers(); function shutdown() { console.log("Shutting down SMTP server..."); watchers.forEach((w) => w.close()); servers.forEach((s) => s.close()); process.exit(0); } ["SIGINT", "SIGTERM", "SIGQUIT"].forEach((signal) => { process.on(signal, shutdown); });