'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} ))}
) : (

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

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