/** * Plain SMTP mailer (nodemailer). Optional: if SMTP not configured, send is a no-op. */ import nodemailer from 'nodemailer' import { readFileSync } from 'fs' import { join, dirname } from 'path' import { fileURLToPath } from 'url' import { log } from '../middleware/logger.mjs' const __dirname = dirname(fileURLToPath(import.meta.url)) let transporter = null function getTransporter() { const host = process.env.SMTP_HOST const user = process.env.SMTP_USER const pass = process.env.SMTP_PASS if (!host || !user || !pass) { return null } if (!transporter) { transporter = nodemailer.createTransport({ host, port: parseInt(process.env.SMTP_PORT || '587', 10), secure: process.env.SMTP_SECURE === 'true', auth: { user, pass }, }) } return transporter } export function renderTemplate(text, vars) { let out = text for (const [k, v] of Object.entries(vars || {})) { out = out.split(`{{${k}}}`).join(v != null ? String(v) : '') } return out } export function loadEmailTemplate(name) { const path = join(__dirname, '..', 'emails', `${name}.txt`) return readFileSync(path, 'utf8') } /** * Send plain-text email. Returns false if SMTP not configured or send failed (logged). */ export async function sendPlainEmail({ to, subject, text }) { const from = process.env.SMTP_FROM || process.env.SMTP_USER if (!to || !subject || !text) { log.warn('sendPlainEmail: missing to/subject/text') return false } const tx = getTransporter() if (!tx) { log.warn('SMTP not configured (SMTP_HOST/SMTP_USER/SMTP_PASS); email skipped') return false } try { await tx.sendMail({ from, to, subject, text }) log.info(`Email sent to ${to}: ${subject}`) return true } catch (e) { log.error('sendPlainEmail failed', { error: e.message, to }) return false } }