"use client"; import { Globe, Loader2, MessageSquare, Save, UserPlus, ExternalLink, Shield, Mail, HardDrive, Ban, Wrench, Palette, FileText, } from "lucide-react"; import { useEffect, useState, type ReactNode } from "react"; import { AdminField, AdminPageHeader, AdminSection, AdminSwitch, } from "@/components/admin"; import { apiUpdateSiteSettings, type PublicSettings } from "@/lib/api"; import { toast } from "@/lib/toast"; type SectionId = | "basic" | "content" | "brand" | "footer" | "security" | "mail" | "storage" | "words" | "maintenance"; const NAV: { id: SectionId; label: string; ready?: boolean }[] = [ { id: "basic", label: "基本信息", ready: true }, { id: "content", label: "内容与互动", ready: true }, { id: "brand", label: "品牌与 SEO" }, { id: "footer", label: "页脚与合规" }, { id: "security", label: "安全与限流" }, { id: "mail", label: "邮件" }, { id: "storage", label: "对象存储" }, { id: "words", label: "敏感词" }, { id: "maintenance", label: "系统维护" }, ]; function SwitchRow({ title, description, control, bordered, }: { title: ReactNode; description: string; control: ReactNode; bordered?: boolean; }) { return (

{title}

{description}

{control}
); } function ComingSoonCard({ title, description, icon, }: { title: string; description: string; icon: ReactNode; }) { return (
{icon}

即将推出

此分区尚未开放配置

); } export default function SettingsAdmin({ initial }: { initial: PublicSettings }) { const [siteName, setSiteName] = useState(initial.site_name); const [siteDescription, setSiteDescription] = useState(initial.site_description); const [allowRegister, setAllowRegister] = useState(initial.allow_register); const [allowComments, setAllowComments] = useState(initial.allow_comments !== false); const [postLinkNewTab, setPostLinkNewTab] = useState(initial.post_link_new_tab !== false); const [savedName, setSavedName] = useState(initial.site_name); const [savedDesc, setSavedDesc] = useState(initial.site_description); const [saving, setSaving] = useState(false); const [togglingReg, setTogglingReg] = useState(false); const [togglingComments, setTogglingComments] = useState(false); const [togglingLink, setTogglingLink] = useState(false); const [active, setActive] = useState("basic"); const dirty = siteName.trim() !== savedName || siteDescription.trim() !== savedDesc; useEffect(() => { const ids = NAV.map((n) => n.id); const observer = new IntersectionObserver( (entries) => { const visible = entries .filter((e) => e.isIntersecting) .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top); if (visible[0]?.target?.id) { const id = visible[0].target.id.replace(/^settings-/, "") as SectionId; if (ids.includes(id)) setActive(id); } }, { rootMargin: "-20% 0px -60% 0px", threshold: [0, 0.25, 0.5] } ); ids.forEach((id) => { const el = document.getElementById(`settings-${id}`); if (el) observer.observe(el); }); return () => observer.disconnect(); }, []); const scrollTo = (id: SectionId) => { setActive(id); document.getElementById(`settings-${id}`)?.scrollIntoView({ behavior: "smooth", block: "start" }); }; const saveIdentity = async () => { if (saving) return; const name = siteName.trim(); if (!name) { toast("站点名称不能为空"); return; } setSaving(true); try { const res = await apiUpdateSiteSettings({ site_name: name, site_description: siteDescription.trim(), }); setSiteName(res.site_name); setSiteDescription(res.site_description); setSavedName(res.site_name); setSavedDesc(res.site_description); toast("站点信息已保存", "ok"); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setSaving(false); } }; const toggleRegister = async () => { if (togglingReg) return; const next = !allowRegister; setTogglingReg(true); try { const res = await apiUpdateSiteSettings({ allow_register: next }); setAllowRegister(res.allow_register); toast(res.allow_register ? "已开放注册" : "已关闭注册", "ok"); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setTogglingReg(false); } }; const toggleComments = async () => { if (togglingComments) return; const next = !allowComments; setTogglingComments(true); try { const res = await apiUpdateSiteSettings({ allow_comments: next }); setAllowComments(res.allow_comments); toast(res.allow_comments ? "已开放评论" : "已关闭评论", "ok"); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setTogglingComments(false); } }; const togglePostLinkNewTab = async () => { if (togglingLink) return; const next = !postLinkNewTab; setTogglingLink(true); try { const res = await apiUpdateSiteSettings({ post_link_new_tab: next }); setPostLinkNewTab(res.post_link_new_tab !== false); toast(res.post_link_new_tab !== false ? "帖子与评论外链将在新标签打开" : "帖子与评论外链将在当前页打开", "ok"); } catch (e) { toast(e instanceof Error ? e.message : "保存失败"); } finally { setTogglingLink(false); } }; return (
开放注册 } description="关闭后,游客将无法提交注册;已有账号仍可登录。适用于内测或维护期。" control={ void toggleRegister()} label="开放注册" /> } /> 开放评论 } description="关闭后前台不展示评论入口、评论数量与已有评论,并提示本站不支持评论。" control={ void toggleComments()} label="开放评论" /> } /> 外链新标签打开 } description="开启后,帖子与评论中的 Markdown 外链在新标签打开;关闭则当前页跳转。" control={ void togglePostLinkNewTab()} label="外链新标签打开" /> } />
} />
} />
} />
} />
} />
} />
); }