"use client" import {useEffect, useState} from "react" import {useMutation, useQueryClient, type UseQueryResult} from "@tanstack/react-query" import {Globe, Info, Loader2, Mail, Search, Server, Sparkles} from "lucide-react" import {Button} from "@/components/ui/button" import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card" import {Input} from "@/components/ui/input" import {Label} from "@/components/ui/label" import {Switch} from "@/components/ui/switch" import {Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle} from "@/components/ui/dialog" import {Badge} from "@/components/ui/badge" import {AdminService} from "@/lib/services" import type {SystemConfig} from "@/lib/services/admin" import {toast} from "sonner" interface SystemTabProps { configs: Record systemConfigsQuery: UseQueryResult } export function SystemTab({ configs, systemConfigsQuery }: SystemTabProps) { const queryClient = useQueryClient() const [serverAddress, setServerAddress] = useState("") const [smtpHost, setSmtpHost] = useState("") const [smtpPort, setSmtpPort] = useState("") const [smtpUsername, setSmtpUsername] = useState("") const [smtpPassword, setSmtpPassword] = useState("") const [smtpTestOpen, setSmtpTestOpen] = useState(false) const [smtpTestTo, setSmtpTestTo] = useState("") const [smtpTestLog, setSmtpTestLog] = useState("") const [smtpTestSuccess, setSmtpTestSuccess] = useState(null) const [smtpTestError, setSmtpTestError] = useState("") useEffect(() => { if (systemConfigsQuery.data) { setServerAddress(configs["server_address"]?.value || "") setSmtpHost(configs["smtp_host"]?.value || "") setSmtpPort(configs["smtp_port"]?.value || "587") setSmtpUsername(configs["smtp_username"]?.value || "") setSmtpPassword(configs["smtp_password"]?.value || "") } }, [systemConfigsQuery.data, configs]) const handleDetectAddress = () => { if (typeof window !== "undefined") { setServerAddress(window.location.origin) toast.success("已自动获取当前域名并填充") } } const updateConfigMutation = useMutation({ mutationFn: async ({ key, value }: { key: string; value: boolean }) => { const config = configs[key] if (!config) { throw new Error(`缺少配置项: ${key}`) } await AdminService.updateSystemConfig(key, { value: value ? "true" : "false", description: config.description, }) }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["admin", "system-configs"] }) await queryClient.invalidateQueries({ queryKey: ["public-config"] }) toast.success("配置已更新") }, onError: (error: Error) => { toast.error(error.message || "更新配置失败") }, }) const saveSystemMutation = useMutation({ mutationFn: async () => { const currentCfg = configs["server_address"] await AdminService.updateSystemConfig("server_address", { value: serverAddress, description: currentCfg?.description || "服务器地址", }) }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["admin", "system-configs"] }) toast.success("通用配置已成功保存") }, onError: (error: Error) => { toast.error(error.message || "保存配置失败") }, }) const handleSystemSave = (e: React.FormEvent) => { e.preventDefault() saveSystemMutation.mutate() } const saveSmtpMutation = useMutation({ mutationFn: async () => { const updates = [ { key: "smtp_host", value: smtpHost }, { key: "smtp_port", value: smtpPort }, { key: "smtp_username", value: smtpUsername }, { key: "smtp_password", value: smtpPassword }, ] for (const update of updates) { const currentCfg = configs[update.key] if (update.key === "smtp_password" && (update.value === "" || update.value === "******")) { // If already configured and sent empty or mask, skip updating it (keep existing) if (currentCfg && currentCfg.value === "******") { continue } } await AdminService.updateSystemConfig(update.key, { value: update.value, description: currentCfg?.description || "", }) } }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["admin", "system-configs"] }) toast.success("SMTP 邮件配置已成功保存") }, onError: (error: Error) => { toast.error(error.message || "保存配置失败") }, }) const handleSmtpSave = (e: React.FormEvent) => { e.preventDefault() saveSmtpMutation.mutate() } const testSmtpMutation = useMutation({ mutationFn: async () => { setSmtpTestLog("正在发起连接测试...\n") setSmtpTestSuccess(null) setSmtpTestError("") const res = await AdminService.testSMTP({ smtp_host: smtpHost, smtp_port: parseInt(smtpPort, 10) || 587, smtp_username: smtpUsername, smtp_password: smtpPassword, to: smtpTestTo, }) return res }, onSuccess: (data) => { setSmtpTestLog(data.log) if (data.success) { setSmtpTestSuccess(true) toast.success("测试邮件发送成功") } else { setSmtpTestSuccess(false) setSmtpTestError(data.error || "发送失败,请检查配置和日志。") toast.error("测试邮件发送失败") } }, onError: (error: Error) => { setSmtpTestSuccess(false) setSmtpTestError(error.message || "请求发送失败") setSmtpTestLog((prev) => prev + `\n[请求错误] ${error.message}\n`) toast.error(error.message || "测试请求发送失败") }, }) const handleSmtpTestSubmit = (e: React.FormEvent) => { e.preventDefault() if (!smtpTestTo) { toast.error("请输入目标邮箱地址") return } testSmtpMutation.mutate() } const indexingEnabled = configs["search_engine_indexing_enabled"]?.value === "true" return (
{/* 通用设置 */}
通用设置 配置系统的全局网络通信访问限制与搜索引擎的公开检索收录参数
{/* 跨域源与服务器地址 */}
访问域名与跨域来源限制
setServerAddress(e.target.value)} placeholder="例如: https://example.com" className="bg-zinc-50/50 dark:bg-zinc-900/50 border-zinc-200 dark:border-zinc-800 text-xs focus-visible:ring-1 focus-visible:ring-indigo-500 transition-all duration-200 h-9.5" />

配置 API 的对外服务访问域名。留空则允许任意源访问(CORS 将开放 `*`,有安全风险)。若配置具体域名,将激活同源及 CORS 白名单验证保护。

{/* SEO 搜索引擎抓取 */}
搜索引擎抓取检索 (SEO)
{indexingEnabled ? "已启用索引" : "已屏蔽检索"}
站点检索可见性开关

控制本系统是否向主流搜索引擎(如 Google、Baidu、Bing)开放公开索引。关闭时,系统响应的 HTML 头部会自动注入 meta robots 标签,防止爬虫收录。

{indexingEnabled ? "允许爬虫访问与收录" : "全面屏蔽外部搜索"} {indexingEnabled ? "爬虫可自由搜集并抓取网站内容" : "已拦截所有网络爬虫的收录请求"}
updateConfigMutation.mutate({ key: "search_engine_indexing_enabled", value: checked }) } className="data-[state=checked]:bg-emerald-500 dark:data-[state=checked]:bg-emerald-600 focus-visible:ring-emerald-500" />
{/* SMTP 邮件设置 */}
SMTP 邮件设置 配置系统的邮件发送服务 (SMTP)
setSmtpHost(e.target.value)} placeholder="例如: smtp.example.com" className="bg-card border-dashed text-xs" />
setSmtpPort(e.target.value)} placeholder="例如: 587 或 465" className="bg-card border-dashed text-xs" />
setSmtpUsername(e.target.value)} placeholder="例如: sender@example.com" className="bg-card border-dashed text-xs" />
setSmtpPassword(e.target.value)} placeholder={configs["smtp_password"]?.value === "******" ? "•••••• (已配置,留空或输入新值)" : "输入凭证密码"} className="bg-card border-dashed text-xs" />
SMTP 发件测试 输入接收测试邮件的邮箱地址。系统将使用您在表单中当前填写的 SMTP 配置进行发件测试。
setSmtpTestTo(e.target.value)} placeholder="例如: receiver@example.com" className="bg-card border-dashed text-xs" disabled={testSmtpMutation.isPending} />
{smtpTestLog && (
                  {smtpTestLog}
                
)} {smtpTestSuccess === true && (
测试成功!邮件已顺利发出。
)} {smtpTestSuccess === false && (
测试失败:{smtpTestError}
)}
) }