summaryrefslogtreecommitdiff
path: root/src/email
diff options
context:
space:
mode:
authorJustZvan <justzvan@justzvan.xyz>2026-02-06 12:16:40 +0100
committerJustZvan <justzvan@justzvan.xyz>2026-02-06 12:16:40 +0100
commite904e9634548e47d611bdcbb88d7b180b927fd5f (patch)
tree21aa5be08fc5b22585508c0263ee5ea4effcc593 /src/email
feat: initial commit!
Diffstat (limited to 'src/email')
-rw-r--r--src/email/confirm.ts75
-rw-r--r--src/email/email.ts66
2 files changed, 141 insertions, 0 deletions
diff --git a/src/email/confirm.ts b/src/email/confirm.ts
new file mode 100644
index 0000000..b885438
--- /dev/null
+++ b/src/email/confirm.ts
@@ -0,0 +1,75 @@
+/** Generates the HTML template for email verification messages */
+export function verificationEmailHtml(verificationCode: string): string {
+ return `
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Buddy Email Verification</title>
+</head>
+<body style="margin:0; padding:0; background-color:#eef2f7; font-family:Arial, Helvetica, sans-serif;">
+ <table width="100%" cellpadding="0" cellspacing="0" role="presentation" style="background-color:#eef2f7; padding:24px 0;">
+ <tr>
+ <td align="center">
+ <table width="600" cellpadding="0" cellspacing="0" role="presentation" style="background-color:#ffffff; border-radius:14px; overflow:hidden; box-shadow:0 10px 25px rgba(0,0,0,0.05);">
+
+ <!-- Header -->
+ <tr>
+ <td style="padding:28px; text-align:center; background-color:#f42e2e;">
+ <h1 style="margin:0; font-size:24px; color:#ffffff;">
+ 🐶 Buddy
+ </h1>
+ <p style="margin:8px 0 0 0; font-size:14px; color:#dbeafe;">
+ Verification code
+ </p>
+ </td>
+ </tr>
+
+ <!-- Content -->
+ <tr>
+ <td style="padding:36px; color:#111827;">
+ <p style="margin:0 0 20px 0; font-size:16px; line-height:1.6;">
+ Enter this code to verify your account:
+ </p>
+
+ <div style="margin:32px 0; text-align:center;">
+ <span style="
+ display:inline-block;
+ font-size:30px;
+ letter-spacing:6px;
+ font-weight:700;
+ padding:18px 26px;
+ border-radius:10px;
+ color:black;
+ border:2px dashed #f42e2e;
+ ">
+ ${verificationCode}
+ </span>
+ </div>
+
+ <p style="margin:0 0 16px 0; font-size:14px; line-height:1.6; color:#374151;">
+ Code expires soon. Didn't request this? Ignore it.
+ </p>
+
+ <p style="margin:0; font-size:14px; color:#6b7280;">
+ Buddy Team
+ </p>
+ </td>
+ </tr>
+
+ <!-- Footer -->
+ <tr>
+ <td style="background-color:#f8fafc; padding:18px; text-align:center; font-size:12px; color:#9ca3af;">
+ © ${new Date().getFullYear()} Buddy
+ </td>
+ </tr>
+
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
+`;
+}
diff --git a/src/email/email.ts b/src/email/email.ts
new file mode 100644
index 0000000..61ec18a
--- /dev/null
+++ b/src/email/email.ts
@@ -0,0 +1,66 @@
+import nodemailer from "nodemailer";
+import { logger } from "../lib/pino";
+
+let transporter: nodemailer.Transporter | null = null;
+
+/**
+ * Gets or creates the nodemailer transporter instance.
+ * Configuration is loaded from SMTP environment variables.
+ */
+export function getTransporter(): nodemailer.Transporter {
+ if (!transporter) {
+ try {
+ if (!process.env.SMTP_HOST) {
+ logger.error("SMTP_HOST environment variable not set");
+ throw new Error("SMTP_HOST is required");
+ }
+
+ if (!process.env.SMTP_PORT) {
+ logger.error("SMTP_PORT environment variable not set");
+ throw new Error("SMTP_PORT is required");
+ }
+
+ if (!process.env.SMTP_USER) {
+ logger.error("SMTP_USER environment variable not set");
+ throw new Error("SMTP_USER is required");
+ }
+
+ if (!process.env.SMTP_PASS) {
+ logger.error("SMTP_PASS environment variable not set");
+ throw new Error("SMTP_PASS is required");
+ }
+
+ const port = Number(process.env.SMTP_PORT);
+ if (isNaN(port) || port <= 0 || port > 65535) {
+ logger.error(
+ { port: process.env.SMTP_PORT },
+ "Invalid SMTP_PORT value",
+ );
+ throw new Error("SMTP_PORT must be a valid port number");
+ }
+
+ transporter = nodemailer.createTransport({
+ host: process.env.SMTP_HOST,
+ port,
+ secure: process.env.SMTP_SECURE == "1",
+ auth: {
+ user: process.env.SMTP_USER,
+ pass: process.env.SMTP_PASS,
+ },
+ });
+
+ logger.info(
+ {
+ host: process.env.SMTP_HOST,
+ port,
+ secure: process.env.SMTP_SECURE == "1",
+ },
+ "Email transporter created successfully",
+ );
+ } catch (error) {
+ logger.error({ error }, "Failed to create email transporter");
+ throw error;
+ }
+ }
+ return transporter;
+}