import type { Dispatch, SetStateAction } from "react"; import { useTranslation } from "react-i18next"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; import { SettingRow } from "@/components/section-card"; import { Database, Lock, RefreshCw, Server, Settings } from "lucide-react"; import { AccordionSection, AdminToggle } from "./AdminSettingsShared"; import { AdminHostTags } from "./AdminHostTags"; import type { TlsStatus } from "@/api/tls-api"; type GeneralSettingsSectionProps = { open: boolean; onToggle: () => void; notificationPrivateEndpoints: string[]; onSaveNotificationPrivateEndpoints: (hosts: string[]) => void; allowRegistration: boolean; handleToggleRegistration: () => void; allowPasswordLogin: boolean; passwordLoginForced?: boolean; handleTogglePasswordLogin: () => void; externalAutoProvision: boolean; handleToggleExternalAutoProvision: () => void; secondFactorAfterExternalLogin: boolean; handleToggleSecondFactorAfterExternalLogin: () => void; allowPasswordReset: boolean; handleTogglePasswordReset: () => void; sessionTimeout: string; setSessionTimeout: Dispatch>; handleSaveSessionTimeout: () => void; statusInterval: string; setStatusInterval: Dispatch>; handleSaveMonitoring: () => void; logLevel: string; handleSaveLogLevel: (level: string) => void; }; export function AdminGeneralSettingsSection({ open, onToggle, notificationPrivateEndpoints, onSaveNotificationPrivateEndpoints, allowRegistration, handleToggleRegistration, allowPasswordLogin, passwordLoginForced, handleTogglePasswordLogin, externalAutoProvision, handleToggleExternalAutoProvision, secondFactorAfterExternalLogin, handleToggleSecondFactorAfterExternalLogin, allowPasswordReset, handleTogglePasswordReset, sessionTimeout, setSessionTimeout, handleSaveSessionTimeout, statusInterval, setStatusInterval, handleSaveMonitoring, logLevel, handleSaveLogLevel, }: GeneralSettingsSectionProps) { const { t } = useTranslation(); return ( } open={open} onToggle={onToggle} >
{t("admin.notificationPrivateEndpoints")} {t("admin.notificationPrivateEndpointsDesc")} onSaveNotificationPrivateEndpoints( event.target.value .split(",") .map((entry) => entry.trim()) .filter(Boolean), ) } />
{passwordLoginForced && (

{t("admin.passwordLoginForced")}

)}
{t("admin.sessionTimeout")}
setSessionTimeout(e.target.value)} className="w-20 text-sm" /> {t("admin.hours")}
{t("admin.sessionTimeoutRange")}
{t("admin.monitoringDefaults")}
setStatusInterval(e.target.value)} className="w-20 text-sm" /> {t("admin.sec")}
{t("admin.logLevel")}
{["debug", "info", "warn", "error"].map((l) => ( ))}
); } type DatabaseSectionProps = { open: boolean; onToggle: () => void; importFile: File | null; setImportFile: Dispatch>; exportLoading: boolean; importLoading: boolean; handleExportDatabase: () => void; handleImportDatabase: () => void; }; export function AdminDatabaseSection({ open, onToggle, importFile, setImportFile, exportLoading, importLoading, handleExportDatabase, handleImportDatabase, }: DatabaseSectionProps) { const { t } = useTranslation(); return ( } open={open} onToggle={onToggle} >
{t("admin.exportDatabase")} {t("admin.exportDatabaseDesc")}
{t("admin.importDatabase")} {importFile ? t("admin.importDatabaseSelected", { name: importFile.name }) : t("admin.importDatabaseDesc")}
setImportFile(e.target.files?.[0] ?? null)} className="absolute inset-0 w-full h-full opacity-0 cursor-pointer" />
{importFile && ( )}
); } type AdminHostDefaultsSectionProps = { open: boolean; onToggle: () => void; }; export function AdminHostDefaultsSection({ open, onToggle, }: AdminHostDefaultsSectionProps) { const { t } = useTranslation(); return ( } open={open} onToggle={onToggle} >
{t("hostDefaults.adminSectionDescription")}
); } function certState( status: TlsStatus | null, ): "none" | "valid" | "expiring" | "expired" { const cert = status?.certificate; if (!cert) return "none"; const left = new Date(cert.notAfter).getTime() - Date.now(); if (left <= 0) return "expired"; if (left < 30 * 86_400_000) return "expiring"; return "valid"; } const CERT_STATUS_STYLES: Record, string> = { none: "text-muted-foreground", valid: "text-green-500", expiring: "text-yellow-500", expired: "text-destructive", }; type AdminSSLSectionProps = { open: boolean; onToggle: () => void; status: TlsStatus | null; manualCertDraft: string; setManualCertDraft: Dispatch>; manualKeyDraft: string; setManualKeyDraft: Dispatch>; manualUploading: boolean; handleManualUpload: () => void; }; export function AdminSSLSection({ open, onToggle, status, manualCertDraft, setManualCertDraft, manualKeyDraft, setManualKeyDraft, manualUploading, handleManualUpload, }: AdminSSLSectionProps) { const { t } = useTranslation(); const cert = status?.certificate ?? null; const state = certState(status); const expiry = cert ? new Date(cert.notAfter).toLocaleDateString() : ""; const certStatusLabel: Record, string> = { none: t("admin.sslCertStatusNone"), valid: t("admin.sslCertStatusValid"), expiring: t("admin.sslCertStatusExpiring"), expired: t("admin.sslCertStatusExpired"), }; return ( } open={open} onToggle={onToggle} >
{t("admin.sslDescription")}{" "} {t("admin.sslDocsLink")}
{t("admin.sslCertStatus")} {certStatusLabel[state]}
{cert && ( <> {t("admin.sslNames", { names: cert.names.join(", ") || "-" })} {t("admin.sslIssuer", { issuer: cert.issuer })} {t("admin.sslCertExpiresAt", { date: expiry })} {cert.selfSigned && ( {t("admin.sslSelfSigned")} )} {status?.renewal && ( {t("admin.sslRenewedBy", { plugin: status.renewal.pluginName, })} )} )}
{cert && !cert.selfSigned && !status?.renewal && (
{t("admin.sslNotRenewed", { date: expiry })}
)} {t("admin.sslManualTitle")}