// @ts-nocheck import React, { useState, useEffect, useCallback } from 'react'; import { adminAPI } from '@/api'; import { formatBjTime } from '@/utils/datetime'; interface Stats { total_configs: number; active_licenses: number; synced_knowledge_bases: number; mode_breakdown: Record; } interface PrivacyConfig { privacy_mode: string; local_llm_provider: string; local_llm_model: string; local_llm_endpoint: string; data_retention_days: number; anonymize_usage: boolean; sync_knowledge_bases: number[]; sync_frequency: string; status: string; last_sync_at?: string; } interface License { id: number; license_key: string; agent_id: number | null; issued_at: string; expires_at: string; max_offline_days: number; status: string; device_fingerprint?: string; } interface KbSyncState { id: number; knowledge_base_id: number; kb_name: string; sync_status: string; total_docs: number; synced_docs: number; sync_direction: string; last_sync_at: string | null; local_version: number; remote_version: number; } interface LocalAgent { id: number; name: string; privacy_mode: string; can_run_local: boolean; llm_provider: string; last_sync: string | null; } interface UsageStats { total_reports: number; unique_users: number; today_reports: number; by_type: Array<{ type: string; count: number }>; } const MODE_OPTIONS = [ { value: 'cloud', label: '云端模式', icon: '☁️', desc: '所有数据和计算在云端处理' }, { value: 'local', label: '本地模式', icon: '🖥️', desc: '数据完全本地处理,隐私优先' }, { value: 'hybrid', label: '混合模式', icon: '🔀', desc: '敏感数据本地,通用能力云端' }, ]; const FREQ_OPTIONS = [ { value: 'realtime', label: '实时' }, { value: 'hourly', label: '每小时' }, { value: 'daily', label: '每天' }, { value: 'manual', label: '手动' }, ]; export default function PrivacySettings() { const [activeTab, setActiveTab] = useState('settings'); const [stats, setStats] = useState(null); const [config, setConfig] = useState(null); const [licenses, setLicenses] = useState([]); const [kbStates, setKbStates] = useState([]); const [localAgents, setLocalAgents] = useState([]); const [usageStats, setUsageStats] = useState(null); const [bridgeConfig, setBridgeConfig] = useState(null); const [loading, setLoading] = useState(false); const [message, setMessage] = useState(''); // Modal states const [showLicenseModal, setShowLicenseModal] = useState(false); const [showSyncModal, setShowSyncModal] = useState(false); const [licenseForm, setLicenseForm] = useState({ agent_id: '', max_offline_days: 7 }); const [syncForm, setSyncForm] = useState({ kb_id: '', direction: 'pull' }); const [newLicense, setNewLicense] = useState(null); const [validatingLicense, setValidatingLicense] = useState(''); const showMsg = (msg: string) => { setMessage(msg); setTimeout(() => setMessage(''), 3000); }; const loadStats = useCallback(async () => { try { const res = await adminAPI.getPrivacyStats(); if (res?.code === 0) setStats(res.data); } catch (e) { console.error('loadStats:', e); } }, []); const loadConfig = useCallback(async () => { try { const res = await adminAPI.getPrivacyConfig(); if (res?.code === 0) setConfig(res.data); } catch (e) { console.error('loadConfig:', e); } }, []); const loadLicenses = useCallback(async () => { try { const res = await adminAPI.getPrivacyLicenses(); if (res?.code === 0) setLicenses(res.data?.items || []); } catch (e) { console.error('loadLicenses:', e); } }, []); const loadKbStates = useCallback(async () => { try { const res = await adminAPI.getKbSyncStates(); if (res?.code === 0) setKbStates(res.data || []); } catch (e) { console.error('loadKbStates:', e); } }, []); const loadLocalAgents = useCallback(async () => { try { const res = await adminAPI.getLocalAgents(); if (res?.code === 0) setLocalAgents(res.data || []); } catch (e) { console.error('loadLocalAgents:', e); } }, []); const loadUsage = useCallback(async () => { try { const res = await adminAPI.getPrivacyUsageStats(); if (res?.code === 0) setUsageStats(res.data); } catch (e) { console.error('loadUsage:', e); } }, []); const loadBridgeConfig = useCallback(async () => { try { const res = await adminAPI.getBridgeConfig(); if (res?.code === 0) setBridgeConfig(res.data); } catch (e) { console.error('loadBridge:', e); } }, []); useEffect(() => { loadStats(); loadConfig(); loadLicenses(); loadKbStates(); loadLocalAgents(); loadUsage(); loadBridgeConfig(); }, [loadStats, loadConfig, loadLicenses, loadKbStates, loadLocalAgents, loadUsage, loadBridgeConfig]); const switchMode = async (mode: string) => { setLoading(true); try { const res = await adminAPI.switchPrivacyMode(mode); if (res?.code === 0) { showMsg(`已切换到${MODE_OPTIONS.find(m => m.value === mode)?.label}`); loadConfig(); loadStats(); } } catch (e) { showMsg('切换失败'); } setLoading(false); }; const updateConfig = async (field: string, value: any) => { setLoading(true); try { const params: any = {}; if (field === 'privacy_mode') params.privacy_mode = value; else if (field === 'local_llm_provider') params.local_llm_provider = value; else if (field === 'local_llm_model') params.local_llm_model = value; else if (field === 'local_llm_endpoint') params.local_llm_endpoint = value; else if (field === 'data_retention_days') params.data_retention_days = value; else if (field === 'sync_frequency') params.sync_frequency = value; const res = await adminAPI.updatePrivacyConfig(params); if (res?.code === 0) { showMsg('配置已更新'); loadConfig(); } } catch (e) { showMsg('更新失败'); } setLoading(false); }; const generateLicense = async () => { setLoading(true); try { const res = await adminAPI.generateOfflineLicense({ agent_id: licenseForm.agent_id || undefined, max_offline_days: licenseForm.max_offline_days, }); if (res?.code === 0) { setNewLicense(res.data); loadLicenses(); loadStats(); showMsg('许可证已生成'); } } catch (e) { showMsg('生成失败'); } setLoading(false); }; const revokeLicense = async (id: number) => { if (!confirm('确定吊销此许可证?')) return; try { await adminAPI.revokePrivacyLicense(id); showMsg('已吊销'); loadLicenses(); loadStats(); } catch (e) { showMsg('吊销失败'); } }; const validateLicenseKey = async () => { if (!validatingLicense) return; setLoading(true); try { const res = await adminAPI.validatePrivacyLicense(validatingLicense); if (res?.data?.valid) showMsg('✅ 许可证有效'); else showMsg('❌ ' + (res?.message || '许可证无效')); } catch (e) { showMsg('验证失败'); } setLoading(false); }; const triggerSync = async () => { if (!syncForm.kb_id) return; setLoading(true); try { const res = await adminAPI.triggerKbSync(parseInt(syncForm.kb_id), syncForm.direction); if (res?.code === 0) { showMsg('同步已触发'); loadKbStates(); loadStats(); setShowSyncModal(false); } } catch (e) { showMsg('同步失败'); } setLoading(false); }; const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text).then(() => showMsg('已复制到剪贴板')); }; const formatDate = (d: string | null) => d ? formatBjTime(d) : '-'; const TABS = [ { key: 'settings', label: '隐私设置', icon: '🔒' }, { key: 'licenses', label: '许可证管理', icon: '🔑' }, { key: 'kb_sync', label: '知识库同步', icon: '🔄' }, { key: 'agents', label: '本地智能体', icon: '🤖' }, { key: 'bridge', label: '平台桥接', icon: '🌉' }, { key: 'usage', label: '用量统计', icon: '📊' }, ]; return (
{/* Header */}

🔐 隐私与本地智能体

管理隐私模式、本地代理、离线许可证和知识库同步

{message && (
{message}
)} {/* Stats Cards */} {stats && (
{[ { label: '隐私配置', value: stats.total_configs, color: '#7c3aed' }, { label: '活跃许可证', value: stats.active_licenses, color: '#059669' }, { label: '已同步知识库', value: stats.synced_knowledge_bases, color: '#2563eb' }, { label: '本地模式', value: stats.mode_breakdown?.local || 0, color: '#ea580c' }, { label: '混合模式', value: stats.mode_breakdown?.hybrid || 0, color: '#ca8a04' }, { label: '云端模式', value: stats.mode_breakdown?.cloud || 0, color: '#6C63FF' }, ].map((s, i) => (
{s.label}
{s.value}
))}
)} {/* Tabs */}
{TABS.map(t => ( ))}
{/* Tab Content */}
{/* ============ TAB: Privacy Settings ============ */} {activeTab === 'settings' && (

隐私模式选择

{MODE_OPTIONS.map(m => (
!loading && switchMode(m.value)} style={{ background: config?.privacy_mode === m.value ? '#F3F0FF' : '#F8F9FC', border: config?.privacy_mode === m.value ? '2px solid #7c3aed' : '2px solid transparent', borderRadius: 10, padding: 20, cursor: 'pointer', transition: 'all 0.2s', opacity: loading ? 0.6 : 1, }} >
{m.icon}
{m.label}
{m.desc}
{config?.privacy_mode === m.value && (
✓ 当前模式
)}
))}
{/* Local LLM Config */} {(config?.privacy_mode === 'local' || config?.privacy_mode === 'hybrid') && (

本地 LLM 配置

updateConfig('local_llm_model', e.target.value)} style={{ width: '100%', padding: 10, background: '#F8F9FC', color: '#1A1F36', border: '1px solid rgba(0,0,0,0.10)', borderRadius: 6, fontSize: 13 }} />
updateConfig('local_llm_endpoint', e.target.value)} style={{ width: '100%', padding: 10, background: '#F8F9FC', color: '#1A1F36', border: '1px solid rgba(0,0,0,0.10)', borderRadius: 6, fontSize: 13 }} />
)} {/* Sync Config */}

同步配置

updateConfig('data_retention_days', parseInt(e.target.value))} style={{ width: '100%', padding: 10, background: '#F8F9FC', color: '#1A1F36', border: '1px solid rgba(0,0,0,0.10)', borderRadius: 6, fontSize: 13 }} />
{/* Status */}
当前状态
模式: {config?.privacy_mode || 'cloud'}
状态: {config?.status || 'active'}
匿名上报: {config?.anonymize_usage ? '已启用' : '已禁用'}
最后同步: {config?.last_sync_at ? formatDate(config.last_sync_at) : '从未'}
)} {/* ============ TAB: Licenses ============ */} {activeTab === 'licenses' && (

离线许可证管理

setValidatingLicense(e.target.value)} style={{ padding: '8px 12px', background: '#F8F9FC', color: '#1A1F36', border: '1px solid rgba(0,0,0,0.10)', borderRadius: 6, fontSize: 13, width: 280 }} />
{/* License Table */} {['ID', '许可证密钥', 'Agent ID', '最大离线天数', '签发时间', '过期时间', '状态', '操作'].map(h => ( ))} {licenses.length === 0 ? ( ) : licenses.map(l => ( ))}
{h}
暂无许可证
{l.id} {l.license_key?.substring(0, 40)}... {l.agent_id || '-'} {l.max_offline_days} 天 {formatDate(l.issued_at)} {formatDate(l.expires_at)} {l.status} {l.status === 'active' && ( )}
{/* License Modal */} {showLicenseModal && (

{newLicense ? '✅ 许可证已生成' : '生成离线许可证'}

{newLicense ? (
许可证密钥 (点击复制)
copyToClipboard(newLicense.license_key)} style={{ fontFamily: 'monospace', fontSize: 11, color: '#4ade80', wordBreak: 'break-all', cursor: 'pointer', background: '#FFFFFF', padding: 10, borderRadius: 6, maxHeight: 100, overflow: 'auto' }}> {newLicense.license_key}
签发时间: {formatDate(newLicense.issued_at ? new Date(newLicense.issued_at * 1000).toISOString() : null)}
过期时间: {formatDate(newLicense.expires_at ? new Date(newLicense.expires_at * 1000).toISOString() : null)}
离线天数: {newLicense.max_offline_days} 天
状态: 活跃
) : (
setLicenseForm({ ...licenseForm, max_offline_days: parseInt(e.target.value) })} style={{ width: '100%' }} />
)}
)}
)} {/* ============ TAB: KB Sync ============ */} {activeTab === 'kb_sync' && (

知识库同步状态

{['知识库', '状态', '方向', '文档数', '已同步', '本地版本', '远程版本', '最后同步'].map(h => ( ))} {kbStates.length === 0 ? ( ) : kbStates.map(kb => ( ))}
{h}
暂无同步记录
{kb.kb_name} {kb.sync_status} {kb.sync_direction} {kb.total_docs} {kb.synced_docs} v{kb.local_version} v{kb.remote_version} {formatDate(kb.last_sync_at)}
{/* Sync Modal */} {showSyncModal && (

触发知识库同步

setSyncForm({ ...syncForm, kb_id: e.target.value })} placeholder="输入知识库 ID" style={{ width: '100%', padding: 10, background: '#F8F9FC', color: '#1A1F36', border: '1px solid rgba(0,0,0,0.10)', borderRadius: 6 }} />
)}
)} {/* ============ TAB: Local Agents ============ */} {activeTab === 'agents' && (

本地智能体状态

{localAgents.length === 0 ? (
暂无已发布的 Agent
) : localAgents.map(agent => (
{agent.name}
ID: {agent.id}
{agent.privacy_mode}
{agent.can_run_local ? ( ✓ 支持本地运行 ({agent.llm_provider || '未配置LLM'}) ) : ( ✗ 仅云端运行 )}
最后同步: {formatDate(agent.last_sync)}
))}
)} {/* ============ TAB: Platform Bridge ============ */} {activeTab === 'bridge' && (

Platform Bridge 配置

将此配置部署到本地 Goose,即可通过 Platform Bridge MCP 扩展连接云端平台。

{bridgeConfig && (
{/* MCP Server Config */}

MCP Client 配置


                    {JSON.stringify(bridgeConfig.mcp_server, null, 2)}

                  
{/* Sync & LLM Config */}

同步 & LLM 配置


                    {JSON.stringify({ sync_config: bridgeConfig.sync_config, local_llm: bridgeConfig.local_llm, privacy_mode: bridgeConfig.privacy_mode }, null, 2)}

                  
{/* Deployment Instructions */}

🚀 部署步骤

  1. 复制 Platform Bridge 代码到本地 ~/.goose/extensions/platform-bridge/
  2. 安装依赖: pip install -r requirements.txt
  3. 配置环境变量: 将上方 MCP Client 配置添加到 Goose
  4. 启动 Goose: 配置将自动连接到平台
)}
)} {/* ============ TAB: Usage ============ */} {activeTab === 'usage' && (

匿名化用量统计

{usageStats && (
总上报数
{usageStats.total_reports}
匿名用户数
{usageStats.unique_users}
今日上报
{usageStats.today_reports}

按类型分布

{usageStats.by_type?.map((t, i) => (
{t.type}
{t.count}
))}

📋 隐私说明

所有用量数据在上报前均已通过 SHA-256 哈希匿名化处理。我们仅收集聚合统计数据,不包含任何可识别个人身份的信息。 数据包括:执行次数、模型调用次数、知识库查询次数、Agent 运行时统计等。

)}
)}
); }