"use client" import {useMemo} from "react" import {useMutation, useQuery, useQueryClient, type UseQueryResult} from "@tanstack/react-query" import {KeyRound, ShieldAlert, X} from "lucide-react" import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card" import {Badge} from "@/components/ui/badge" import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select" import services from "@/lib/services" import type {SystemConfig} from "@/lib/services/admin" import {TemplatesManager} from "./templates" import {toast} from "sonner" interface OperationTabProps { configs: Record systemConfigsQuery: UseQueryResult } export function OperationTab({ configs, systemConfigsQuery }: OperationTabProps) { const queryClient = useQueryClient() const uploadTypesQuery = useQuery({ queryKey: ["admin", "upload-types"], queryFn: () => services.adminSystemConfig.listUploadTypes(), }) const updateWhitelistMutation = useMutation({ mutationFn: async (newValue: string) => { const config = configs["file_access_whitelist"] if (!config) { throw new Error("缺少配置项: file_access_whitelist") } await services.adminSystemConfig.updateSystemConfig("file_access_whitelist", { value: newValue, 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 whitelistConfig = configs["file_access_whitelist"] const currentWhitelist = useMemo(() => { if (!whitelistConfig?.value) return ["avatar"] try { const parsed = JSON.parse(whitelistConfig.value) if (Array.isArray(parsed)) return parsed } catch { // 降级支持逗号分隔解析 return whitelistConfig.value.split(",").map(s => s.trim()).filter(Boolean) } return ["avatar"] }, [whitelistConfig?.value]) const handleAddType = (type: string) => { if (!type || currentWhitelist.includes(type)) return const newWhitelist = [...currentWhitelist, type] updateWhitelistMutation.mutate(JSON.stringify(newWhitelist)) } const handleRemoveType = (typeToRemove: string) => { const newWhitelist = currentWhitelist.filter(t => t !== typeToRemove) updateWhitelistMutation.mutate(JSON.stringify(newWhitelist)) } const availableTypes = useMemo(() => { const types = uploadTypesQuery.data ?? [] return types.map(t => { let label = t if (t === "avatar") label = "头像 (avatar)" else if (t === "attachment") label = "附件 (attachment)" else if (t === "doc") label = "文档 (doc)" else if (t === "generic") label = "通用 (generic)" return { value: t, label } }) }, [uploadTypesQuery.data]) return (
{/* 文件访问白名单设置 */}
文件访问权限控制 配置免登录直接访问的文件业务类型。不在白名单内的文件将要求登录鉴权。
添加免鉴权类型:
{/* 当前白名单列表 */}
当前免鉴权列表
{currentWhitelist.length > 0 ? (
{currentWhitelist.map(type => ( {availableTypes.find(t => t.value === type)?.label || type} ))}
) : (

白名单已空,所有类型文件的访问都将需要登录。

)}
{/* 通知模板管理 */}
) }