import net from 'node:net'; import { env } from '../config/env.js'; export interface EmailDiagnosticsResult { host: string; port: number; from: string; smtpAuth: boolean; startTls: boolean; usernameConfigured: boolean; passwordConfigured: boolean; ok: boolean; reachable: boolean; elapsedMs: number; checkedAt: string; error: string | null; } function mailHost(): string { return String(process.env.ACS_MAIL_HOST ?? env.mailHost ?? '').trim(); } function mailPort(): number { const port = Number(process.env.ACS_MAIL_PORT ?? env.mailPort); return Number.isFinite(port) && port > 0 ? port : 25; } function mailFrom(): string { return String(process.env.ACS_MAIL_FROM ?? env.mailFrom ?? '').trim(); } function mailUsername(): string { return String(process.env.ACS_MAIL_USERNAME ?? env.mailUsername ?? '').trim(); } function mailPassword(): string { return String(process.env.ACS_MAIL_PASSWORD ?? env.mailPassword ?? '').trim(); } function mailSmtpAuth(): boolean { return (process.env.ACS_MAIL_SMTP_AUTH ?? String(env.mailSmtpAuth)).toLowerCase() === 'true'; } function mailStartTls(): boolean { return (process.env.ACS_MAIL_STARTTLS ?? String(env.mailStartTls)).toLowerCase() === 'true'; } function mailTimeoutMs(): number { const timeout = Number(process.env.ACS_MAIL_TIMEOUT_MS ?? env.mailTimeoutMs); return Number.isFinite(timeout) && timeout > 0 ? timeout : 10000; } function result(partial: Partial): EmailDiagnosticsResult { return { host: mailHost(), port: mailPort(), from: mailFrom(), smtpAuth: mailSmtpAuth(), startTls: mailStartTls(), usernameConfigured: Boolean(mailUsername()), passwordConfigured: Boolean(mailPassword()), ok: false, reachable: false, elapsedMs: 0, checkedAt: new Date().toISOString(), error: null, ...partial, }; } function connectSmtp(host: string, port: number, timeoutMs: number): Promise { return new Promise((resolve, reject) => { const socket = net.createConnection({ host, port }); let settled = false; const finish = (error?: Error) => { if (settled) return; settled = true; socket.destroy(); if (error) reject(error); else resolve(); }; socket.setTimeout(timeoutMs); socket.once('connect', () => finish()); socket.once('timeout', () => finish(new Error(`SMTP connection timed out after ${timeoutMs} ms`))); socket.once('error', (error) => finish(error)); }); } export async function diagnoseEmailConnectivity(): Promise { const host = mailHost(); const port = mailPort(); const startedAt = Date.now(); if (!host) { return result({ ok: false, reachable: false, checkedAt: new Date().toISOString(), error: 'ACS_MAIL_HOST가 설정되지 않았습니다.', }); } try { await connectSmtp(host, port, mailTimeoutMs()); return result({ ok: true, reachable: true, elapsedMs: Date.now() - startedAt, checkedAt: new Date().toISOString(), }); } catch (error) { const message = error instanceof Error ? error.message : String(error); return result({ ok: false, reachable: false, elapsedMs: Date.now() - startedAt, checkedAt: new Date().toISOString(), error: message.slice(0, 500), }); } }