diff --git a/src/App.tsx b/src/App.tsx index 78f9e07..0cccfae 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,7 @@ const KnowledgeBasesPage = lazy(() => import('./pages/KnowledgeBases')) const BillingPage = lazy(() => import('./pages/Billing')) const GiteaEmbed = lazy(() => import('./pages/GiteaEmbed')) const ConfigPage = lazy(() => import("./pages/Config")) +const SecretsPage = lazy(() => import('./pages/Secrets')) const MembershipPage = lazy(() => import("./pages/Membership")) const FilesAdminPage = lazy(() => import("./pages/FilesAdmin")) const AiGatewayPage = lazy(() => import("./pages/AiGateway")) @@ -72,6 +73,10 @@ function App() { } /> } /> + }>} + /> , requiredRole: 'SUPER_ADMIN' }, { path: '/git', name: '代码仓库', icon: }, { path: '/ops', name: '系统运维', icon: , requiredRole: 'SUPER_ADMIN', children: [ + { path: '/secrets', name: '密钥管理' }, { path: '/metrics', name: '接口监控' }, { path: '/ai-gateway', name: 'AI Gateway' }, { path: '/servers', name: '服务器' }, diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx index f9fd636..b5fff1b 100644 --- a/src/layouts/AdminLayout.tsx +++ b/src/layouts/AdminLayout.tsx @@ -25,6 +25,7 @@ const breadcrumbMap: Record = { '/git': '代码仓库', '/servers': '服务器运维', '/config': '配置管理', + '/secrets': '密钥管理', '/metrics': '接口监控', '/ai-gateway': 'AI Gateway', '/safety': '内容安全', diff --git a/src/pages/Secrets.tsx b/src/pages/Secrets.tsx new file mode 100644 index 0000000..3a77fbc --- /dev/null +++ b/src/pages/Secrets.tsx @@ -0,0 +1,65 @@ +import { useState } from 'react' +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { Table, Button, Typography, App, Modal, Input, Select, Tag, Tabs, DatePicker } from 'antd' +import { ReloadOutlined, PlusOutlined, DeleteOutlined, KeyOutlined } from '@ant-design/icons' +import { api } from '@/services/http-client' +import dayjs from 'dayjs' + +const { Title, Text } = Typography + +function SecretsPage() { + const { modal, message } = App.useApp() + const qc = useQueryClient() + const [addOpen, setAddOpen] = useState(false) + const [form, setForm] = useState({ name: '', provider: 'deepseek', value: '', expiresAt: '' }) + + const { data: secrets } = useQuery({ queryKey: ['secrets'], queryFn: (): Promise => api.get('/admin-api/secrets') }) + const { data: logs } = useQuery({ queryKey: ['secrets', 'logs'], queryFn: (): Promise => api.get('/admin-api/secrets/logs') }) + + const addSecret = async () => { + await api.post('/admin-api/secrets', form) + message.success('已添加') + setAddOpen(false); qc.invalidateQueries({ queryKey: ['secrets'] }) + } + + const deleteSecret = (id: string) => modal.confirm({ + title: '删除密钥', okType: 'danger', + onOk: async () => { await api.delete(`/admin-api/secrets/${id}`); qc.invalidateQueries({ queryKey: ['secrets'] }) }, + }) + + const secCols = [ + { title: '名称', dataIndex: 'name', width: 150 }, + { title: '服务商', dataIndex: 'provider', width: 100, render: (v: string) => {v} }, + { title: '末四位', dataIndex: 'maskLast4', width: 80, render: (v: string) => ****{v} }, + { title: '状态', dataIndex: 'status', width: 70, render: (v: string) => {v} }, + { title: '到期', dataIndex: 'expiresAt', width: 100, render: (d: string) => d ? dayjs(d).format('MM-DD') : 永久 }, + { title: '操作', width: 80, render: (_:any, r:any) => + + }, + { key: 'logs', label: '访问日志', children: }, + ]} /> + setAddOpen(false)} okText="添加"> + setForm({...form, name: e.target.value})} style={{ marginBottom: 12 }} /> +