import nodemailer, { type Transporter } from 'nodemailer'; import { env } from '@/lib/env'; import { logger } from '@/lib/logger'; /** * Creates and returns a new Nodemailer SMTP transporter. * * A new instance is created on each call so the factory can be used in * contexts where connection pooling is managed externally (e.g. per-request * in serverless, or once at worker startup). */ export function createTransporter(): Transporter { return nodemailer.createTransport({ host: env.SMTP_HOST, port: env.SMTP_PORT, // Implicitly secure when port is 465; STARTTLS for all other ports. secure: env.SMTP_PORT === 465, }); } export interface SendEmailOptions { to: string | string[]; subject: string; html: string; from?: string; } /** * Sends a single email via SMTP. * * Returns the nodemailer info object on success. Propagates errors to the * caller — callers in background jobs should wrap in try/catch and handle * retries via BullMQ. */ export async function sendEmail( to: string | string[], subject: string, html: string, from?: string, ): Promise { const transporter = createTransporter(); const info = await transporter.sendMail({ from: from ?? `Port Nimara CRM `, to: Array.isArray(to) ? to.join(', ') : to, subject, html, }); logger.debug( { messageId: info.messageId, to, subject }, 'Email sent', ); return info; }