lilith-platform.live/codebase/@features/admin/backend-api/src/mailer.ts
Claude Code f87e7572de db(migrations): 🗃️ Apply backend API database migration for admin schema updates
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-04-05 03:04:38 -07:00

54 lines
2.2 KiB
TypeScript

/**
* Booking mailer — SMTP transport for booking response emails sent from admin.
*
* Env vars (falls back to SMTP_* contact-form vars if BOOKING_* not set):
* BOOKING_SMTP_USER — SMTP username (default: SMTP_USER)
* BOOKING_SMTP_PASS — SMTP password (default: SMTP_PASS)
* BOOKING_FROM_EMAIL — Sender address (default: Quinn Bookings <booking@transquinnftw.com>)
* BOOKING_NOTIFY_EMAIL — Provider notification address (default: TransQuinnFTW@pm.me)
* SMTP_HOST — SMTP server host (default: localhost)
* SMTP_PORT — SMTP port (default: 587)
* SMTP_REQUIRE_TLS — Enforce STARTTLS; set "false" for dev (default: true)
*/
import nodemailer from 'nodemailer';
import type { Transporter } from 'nodemailer';
export const BOOKING_FROM = process.env['BOOKING_FROM_EMAIL'] ?? 'Quinn Bookings <booking@transquinnftw.com>';
export const BOOKING_NOTIFY = process.env['BOOKING_NOTIFY_EMAIL'] ?? 'TransQuinnFTW@pm.me';
const REQUIRE_TLS = process.env['SMTP_REQUIRE_TLS'] !== 'false';
let cachedTransport: Transporter | null = null;
export function getBookingTransport(): Transporter {
if (cachedTransport) return cachedTransport;
const user = process.env['BOOKING_SMTP_USER'] ?? process.env['SMTP_USER'];
const pass = process.env['BOOKING_SMTP_PASS'] ?? process.env['SMTP_PASS'] ?? '';
cachedTransport = nodemailer.createTransport({
host: process.env['SMTP_HOST'] ?? 'localhost',
port: parseInt(process.env['SMTP_PORT'] ?? '587', 10),
secure: false, // STARTTLS on 587
auth: user ? { user, pass } : undefined,
requireTLS: REQUIRE_TLS,
tls: {
rejectUnauthorized: REQUIRE_TLS,
minVersion: 'TLSv1.2' as const,
},
});
return cachedTransport;
}
export function escapeHtml(str: string): string {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;');
}
/** Replace {{key}} tokens in a template string with the values map. */
export function interpolate(template: string, vars: Record<string, string>): string {
return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => vars[key] ?? '');
}