panels-origin/api/smtp.ts

159 lines
4.5 KiB
TypeScript

import type { PlatformDb } from "./platform_db.ts";
import { config } from "./config.ts";
export type SmtpSettings = {
host: string;
port: number;
username: string;
password: string;
from_address: string;
enabled: boolean;
updated_at?: string;
};
export type SmtpPublic = {
host: string;
port: number;
username: string;
from_address: string;
enabled: boolean;
configured: boolean;
has_password: boolean;
updated_at: string | null;
source: "db" | "env" | "none";
};
type SmtpRow = {
host: string;
port: number;
username: string;
password: string;
from_address: string;
enabled: number;
updated_at: string;
};
function trim(v: unknown): string {
return (v ?? "").toString().trim();
}
function fromEnv(): SmtpSettings {
return {
host: config.smtpHost,
port: config.smtpPort || 587,
username: config.smtpUser,
password: config.smtpPass,
from_address: config.smtpFrom,
enabled: Boolean(config.smtpHost && config.smtpFrom),
};
}
function readRow(platformDb: PlatformDb): SmtpRow | undefined {
try {
return platformDb.prepare(
`SELECT host, port, username, password, from_address, enabled, updated_at
FROM smtp_settings WHERE id = 1`,
).get() as SmtpRow | undefined;
} catch {
return undefined;
}
}
/** Config efectiva: fila en platform.db si hay host; si no, variables de entorno. */
export function resolveSmtp(platformDb: PlatformDb): SmtpSettings & { source: "db" | "env" | "none" } {
const row = readRow(platformDb);
if (row && trim(row.host)) {
return {
host: trim(row.host),
port: Number(row.port) || 587,
username: trim(row.username),
password: row.password ?? "",
from_address: trim(row.from_address) || config.smtpFrom,
enabled: Number(row.enabled) === 1,
updated_at: row.updated_at,
source: "db",
};
}
const env = fromEnv();
if (env.host) return { ...env, source: "env" };
return { ...env, enabled: false, source: "none" };
}
export function smtpConfigured(platformDb: PlatformDb): boolean {
const s = resolveSmtp(platformDb);
return s.enabled && Boolean(s.host && s.from_address);
}
export function smtpPublicView(platformDb: PlatformDb): SmtpPublic {
const s = resolveSmtp(platformDb);
return {
host: s.host,
port: s.port,
username: s.username,
from_address: s.from_address,
enabled: s.enabled,
configured: smtpConfigured(platformDb),
has_password: Boolean(s.password),
updated_at: s.updated_at ?? null,
source: s.source,
};
}
export type SaveSmtpInput = {
host?: string;
port?: number | string;
username?: string;
password?: string;
from_address?: string;
enabled?: boolean;
/** Si true y password vacío, conserva la contraseña actual. */
keep_password?: boolean;
};
function looksLikeEmailFrom(value: string): boolean {
// Acepta "user@dom.com" o "Nombre <user@dom.com>"
return /(?:^|<)[^\s<>@]+@[^\s<>@]+\.[^\s<>@]+(?:>|$)/.test(value);
}
export function saveSmtpSettings(
platformDb: PlatformDb,
input: SaveSmtpInput,
): { error?: string; settings?: SmtpPublic } {
const host = trim(input.host);
const fromAddress = trim(input.from_address);
const username = trim(input.username);
const port = Number(input.port ?? 587);
if (!Number.isFinite(port) || port < 1 || port > 65535) {
return { error: "Puerto SMTP inválido" };
}
const enabled = input.enabled !== false;
if (enabled && !host) return { error: "Host SMTP obligatorio si está habilitado" };
if (enabled && !fromAddress) return { error: "Remitente (From) obligatorio si está habilitado" };
if (enabled && fromAddress && !looksLikeEmailFrom(fromAddress)) {
return {
error: 'Remitente inválido. Use un correo, ej. PANELS <noreply@tudominio.com>',
};
}
const current = readRow(platformDb);
let password = input.password ?? "";
if ((input.keep_password || password === "") && current?.password) {
password = current.password;
}
platformDb.prepare(
`INSERT INTO smtp_settings (id, host, port, username, password, from_address, enabled, updated_at)
VALUES (1, ?, ?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT(id) DO UPDATE SET
host = excluded.host,
port = excluded.port,
username = excluded.username,
password = excluded.password,
from_address = excluded.from_address,
enabled = excluded.enabled,
updated_at = datetime('now')`,
).run(host, port, username, password, fromAddress, enabled ? 1 : 0);
return { settings: smtpPublicView(platformDb) };
}