feat: add email diagnostics
This commit is contained in:
123
server/services/mail.ts
Normal file
123
server/services/mail.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
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>): 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<void> {
|
||||
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<EmailDiagnosticsResult> {
|
||||
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),
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user