import { getDb } from "./db"; import { canEncrypt, decryptSecret, encryptSecret } from "./settings-crypto"; export const INTEGRATION_SECTIONS = ["smtp", "telegram", "logo"] as const; export type IntegrationSection = (typeof INTEGRATION_SECTIONS)[number]; const SECRET_KEYS = new Set(["smtp_pass", "telegram_bot_token", "logo_dev_secret_key"]); const ENV_FALLBACK: Record = { smtp_host: "SMTP_HOST", smtp_port: "SMTP_PORT", smtp_user: "SMTP_USER", smtp_pass: "SMTP_PASS", mail_from: "MAIL_FROM", telegram_bot_token: "TELEGRAM_BOT_TOKEN", logo_dev_secret_key: "LOGO_DEV_SECRET_KEY", logo_dev_publishable_key: "LOGO_DEV_PUBLISHABLE_KEY", }; export type SmtpSettings = { host: string; port: number; user: string; pass: string; from: string }; export type TelegramSettings = { botToken: string }; export type LogoSettings = { secretKey: string; publishableKey: string }; async function readStored(key: string): Promise { const db = getDb(); const row = await db.appSetting.findUnique({ where: { key } }); if (!row) return null; if (SECRET_KEYS.has(key)) { try { return decryptSecret(row.value); } catch { return null; } } return row.value; } async function resolveValue(key: string): Promise { const stored = await readStored(key); if (stored !== null && stored !== "") return stored; return process.env[ENV_FALLBACK[key] ?? ""] ?? ""; } /** Volle SMTP-Konfiguration: UI-Werte zuerst, sonst .env-Rückfall. */ export async function getSmtpSettings(): Promise { const [host, port, user, pass, from] = await Promise.all([ resolveValue("smtp_host"), resolveValue("smtp_port"), resolveValue("smtp_user"), resolveValue("smtp_pass"), resolveValue("mail_from"), ]); return { host, port: Number(port) || 587, user, pass, from }; } export async function getTelegramSettings(): Promise { return { botToken: await resolveValue("telegram_bot_token") }; } export async function getLogoSettings(): Promise { const [secretKey, publishableKey] = await Promise.all([ resolveValue("logo_dev_secret_key"), resolveValue("logo_dev_publishable_key"), ]); return { secretKey, publishableKey }; } /** Status für die UI: Klartextwerte plus je Geheimnis nur ob gesetzt, nie der Wert. */ export async function getIntegrationStatus() { const [smtp, telegram, logo] = await Promise.all([getSmtpSettings(), getTelegramSettings(), getLogoSettings()]); return { smtp: { host: smtp.host, port: smtp.port, user: smtp.user, from: smtp.from, passGesetzt: smtp.pass !== "" }, telegram: { botTokenGesetzt: telegram.botToken !== "" }, logo: { secretGesetzt: logo.secretKey !== "", publishableKey: logo.publishableKey }, verschluesselungBereit: canEncrypt(), }; } const SECTION_FIELDS: Record = { smtp: ["smtp_host", "smtp_port", "smtp_user", "smtp_pass", "mail_from"], telegram: ["telegram_bot_token"], logo: ["logo_dev_secret_key", "logo_dev_publishable_key"], }; export function validateIntegrationSection(section: string, values: Record): string[] { if (!(INTEGRATION_SECTIONS as readonly string[]).includes(section)) return ["section"]; const errs: string[] = []; if (section === "smtp") { if (typeof values.smtp_port !== "undefined" && values.smtp_port !== "" && !Number.isInteger(Number(values.smtp_port))) { errs.push("smtp_port"); } if (typeof values.smtp_host === "string" && values.smtp_host.trim() !== "" && values.smtp_host.trim().length > 255) { errs.push("smtp_host"); } } for (const key of SECTION_FIELDS[section as IntegrationSection]) { const v = values[key]; if (typeof v !== "undefined" && typeof v !== "string") errs.push(key); if (typeof v === "string" && v.length > 2000) errs.push(key); } return errs; } /** * Speichert einen Bereich. Leere Geheimnis-Felder lassen den bestehenden Wert * unverändert (UI sendet dann ""), alle anderen Felder werden übernommen * ("" löscht einen Klartextwert). Gibt die gespeicherten Schlüssel zurück. */ export async function saveIntegrationSection(section: IntegrationSection, values: Record): Promise { const db = getDb(); const saved: string[] = []; for (const key of SECTION_FIELDS[section]) { if (!(key in values)) continue; const v = values[key]; if (SECRET_KEYS.has(key)) { if (v === "") continue; await db.appSetting.upsert({ where: { key }, create: { key, value: encryptSecret(v) }, update: { value: encryptSecret(v) } }); } else { await db.appSetting.upsert({ where: { key }, create: { key, value: v }, update: { value: v } }); } saved.push(key); } return saved; }