import nodemailer from "nodemailer"; import { config } from "./config.js"; import type { ContactRequest } from "./validation.js"; const transporter = nodemailer.createTransport({ host: config.SMTP_HOST, port: config.SMTP_PORT, secure: config.SMTP_SECURE, requireTLS: config.SMTP_REQUIRE_TLS, auth: { user: config.SMTP_USER, pass: config.SMTP_PASS, }, tls: { rejectUnauthorized: true, }, }); const escapeHtml = (value: string): string => value .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); const newlineToBreaks = (value: string): string => escapeHtml(value).replace(/\n/g, "
"); export async function verifyMailerConnection(): Promise { await transporter.verify(); } export async function sendContactEmail(payload: ContactRequest, sourceIp: string): Promise { const receivedAt = new Date().toISOString(); const subject = `${config.MAIL_SUBJECT_PREFIX} ${payload.name}`; const textBody = [ `Name: ${payload.name}`, `Email: ${payload.email}`, `Source IP: ${sourceIp || "unknown"}`, `Received At: ${receivedAt}`, "", "Message:", payload.message, ].join("\n"); const htmlBody = `

Portfolio Contact Submission

Name: ${escapeHtml(payload.name)}

Email: ${escapeHtml(payload.email)}

Source IP: ${escapeHtml(sourceIp || "unknown")}

Received At: ${escapeHtml(receivedAt)}

Message:
${newlineToBreaks(payload.message)}

`; await transporter.sendMail({ from: `"${config.MAIL_FROM_NAME}" <${config.MAIL_FROM_ADDRESS}>`, to: config.MAIL_TO_ADDRESS, replyTo: payload.email, subject, text: textBody, html: htmlBody, }); }