"use client"; import { useState, useEffect, useCallback } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Switch } from "@/components/ui/switch"; import { Alert, AlertDescription } from "@/components/ui/alert"; // ── Types ───────────────────────────────────────────────────────────────────── interface SMTPOutConfig { id?: number; enabled: boolean; host: string; port: number; user: string; password: string; tls: boolean; from: string; updated_at?: string; updated_by?: string; } const defaultForm: SMTPOutConfig = { enabled: true, host: "", port: 587, user: "", password: "", tls: false, from: "", }; // ── API helpers ─────────────────────────────────────────────────────────────── async function fetchConfig(): Promise<{ configured: boolean; config?: SMTPOutConfig }> { const res = await fetch("/api/admin/smtp-out", { credentials: "include" }); if (!res.ok) throw new Error("Laden fehlgeschlagen"); return res.json(); } async function saveConfig(cfg: SMTPOutConfig): Promise { const res = await fetch("/api/admin/smtp-out", { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(cfg), }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error((err as { error?: string }).error ?? "Speichern fehlgeschlagen"); } } async function deleteConfig(): Promise { const res = await fetch("/api/admin/smtp-out", { method: "DELETE", credentials: "include" }); if (!res.ok) throw new Error("Löschen fehlgeschlagen"); } async function testConfig(): Promise { const res = await fetch("/api/admin/smtp-out/test", { method: "POST", credentials: "include" }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error((data as { error?: string }).error ?? "Test fehlgeschlagen"); return (data as { sent_to?: string }).sent_to ?? ""; } // ── Component ───────────────────────────────────────────────────────────────── export function SMTPOutTab() { const [configured, setConfigured] = useState(false); const [form, setForm] = useState(defaultForm); const [changePassword, setChangePassword] = useState(false); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [testing, setTesting] = useState(false); const [testResult, setTestResult] = useState<{ ok: boolean; msg: string } | null>(null); const [error, setError] = useState(""); const [updatedBy, setUpdatedBy] = useState(""); const [updatedAt, setUpdatedAt] = useState(""); const load = useCallback(() => { setLoading(true); fetchConfig() .then((data) => { setConfigured(data.configured); if (data.config) { setForm({ ...data.config, password: "" }); setUpdatedBy(data.config.updated_by ?? ""); setUpdatedAt(data.config.updated_at ?? ""); } else { setForm(defaultForm); } setChangePassword(!data.configured); }) .catch(() => setError("Konfiguration konnte nicht geladen werden")) .finally(() => setLoading(false)); }, []); useEffect(() => { load(); }, [load]); const handleSave = async (e: React.FormEvent) => { e.preventDefault(); setError(""); setSaving(true); try { const payload = { ...form }; if (!changePassword) payload.password = ""; await saveConfig(payload); setTestResult(null); load(); } catch (e: unknown) { setError(e instanceof Error ? e.message : "Fehler"); } finally { setSaving(false); } }; const handleTest = async () => { setError(""); setTestResult(null); setTesting(true); try { const sentTo = await testConfig(); setTestResult({ ok: true, msg: `Test-E-Mail gesendet an ${sentTo}` }); } catch (e: unknown) { setTestResult({ ok: false, msg: e instanceof Error ? e.message : "Test fehlgeschlagen" }); } finally { setTesting(false); } }; const handleDelete = async () => { if (!confirm("SMTP-Out-Konfiguration wirklich löschen?")) return; setError(""); try { await deleteConfig(); load(); } catch (e: unknown) { setError(e instanceof Error ? e.message : "Fehler"); } }; const set = (field: keyof SMTPOutConfig, value: unknown) => setForm((prev) => ({ ...prev, [field]: value })); return (
SMTP-Out Relay {configured && form.enabled ? "Aktiv" : configured ? "Deaktiviert" : "Nicht konfiguriert"}

Ausgehender SMTP-Relay für Signup-Bestätigungen, Passwort-Reset und Einladungslinks. Das Passwort wird verschlüsselt gespeichert. {configured && updatedBy && ( Zuletzt geändert von {updatedBy} {updatedAt && ` am ${new Date(updatedAt).toLocaleDateString("de-DE")}`}. )}

{error && ( {error} )} {loading ? (

Lädt...

) : (
{/* Enabled toggle */}
set("enabled", v)} />
set("host", e.target.value)} required />
set("port", parseInt(e.target.value, 10) || 587)} />
set("user", e.target.value)} autoComplete="username" />
set("from", e.target.value)} required />
{/* Password */}
{configured && (
setChangePassword(e.target.checked)} />
)} {(!configured || changePassword) && (
set("password", e.target.value)} autoComplete="new-password" />
)}
{/* TLS */}
set("tls", v)} />
{/* Test result */} {testResult && ( {testResult.msg} )} {/* Actions */}
{configured && ( )}
)}
); }