diff --git a/src/App.tsx b/src/App.tsx index 0cccfae..708344a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,8 @@ 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 SecurityEventsPage = lazy(() => import('./pages/SecurityEvents')) +const ThrottlePage = lazy(() => import('./pages/Throttle')) const SecretsPage = lazy(() => import('./pages/Secrets')) const MembershipPage = lazy(() => import("./pages/Membership")) const FilesAdminPage = lazy(() => import("./pages/FilesAdmin")) @@ -73,6 +75,14 @@ function App() { } /> } /> + }>} + /> + }>} + /> }>} diff --git a/src/config/menu.tsx b/src/config/menu.tsx index 21b3dfc..5148b02 100644 --- a/src/config/menu.tsx +++ b/src/config/menu.tsx @@ -30,6 +30,8 @@ export const adminMenuItems: AdminMenuItem[] = [ { path: '/billing', name: 'API 用量', icon: , requiredRole: 'SUPER_ADMIN' }, { path: '/git', name: '代码仓库', icon: }, { path: '/ops', name: '系统运维', icon: , requiredRole: 'SUPER_ADMIN', children: [ + { path: '/throttle', name: '限流管理' }, + { path: '/security-events', name: '安全事件' }, { path: '/secrets', name: '密钥管理' }, { path: '/metrics', name: '接口监控' }, { path: '/ai-gateway', name: 'AI Gateway' }, diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx index b5fff1b..56bb465 100644 --- a/src/layouts/AdminLayout.tsx +++ b/src/layouts/AdminLayout.tsx @@ -25,6 +25,8 @@ const breadcrumbMap: Record = { '/git': '代码仓库', '/servers': '服务器运维', '/config': '配置管理', + '/throttle': '限流管理', + '/security-events': '安全事件', '/secrets': '密钥管理', '/metrics': '接口监控', '/ai-gateway': 'AI Gateway', diff --git a/src/pages/SecurityEvents.tsx b/src/pages/SecurityEvents.tsx new file mode 100644 index 0000000..147cc80 --- /dev/null +++ b/src/pages/SecurityEvents.tsx @@ -0,0 +1,31 @@ +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { Table, Button, Typography, Tag, App } from 'antd' +import { ReloadOutlined, SafetyOutlined } from '@ant-design/icons' +import { api } from '@/services/http-client' +import dayjs from 'dayjs' + +const { Title } = Typography + +export default function SecurityEventsPage() { + const qc = useQueryClient() + const { data } = useQuery({ queryKey: ['security-events'], queryFn: (): Promise => api.get('/admin-api/content-safety/checks'), staleTime: 10_000 }) + + const cols = [ + { title: '时间', dataIndex: 'createdAt', width: 140, render: (d: string) => dayjs(d).format('MM-DD HH:mm:ss') }, + { title: '类型', dataIndex: 'contentType', width: 100 }, + { title: '用户', dataIndex: 'userId', width: 100, ellipsis: true }, + { title: '风险', dataIndex: 'riskLevel', width: 70, render: (v: string) => {v} }, + { title: '结果', dataIndex: 'result', width: 80, render: (v: string) => {v} }, + { title: '匹配词', dataIndex: 'matchedWords', ellipsis: true, width: 150, render: (v: string) => v || '-' }, + ] + + return ( +
+
+ <SafetyOutlined /> 安全事件 + +
+ + + ) +} diff --git a/src/pages/Throttle.tsx b/src/pages/Throttle.tsx new file mode 100644 index 0000000..b0fd897 --- /dev/null +++ b/src/pages/Throttle.tsx @@ -0,0 +1,34 @@ +import { useQuery, useQueryClient } from '@tanstack/react-query' +import { Card, Row, Col, Statistic, Table, Button, Typography, Tag } from 'antd' +import { ReloadOutlined, ThunderboltOutlined } from '@ant-design/icons' +import { api } from '@/services/http-client' + +const { Title } = Typography + +export default function ThrottlePage() { + const qc = useQueryClient() + const { data } = useQuery({ queryKey: ['throttle'], queryFn: (): Promise => api.get('/admin-api/throttle/status'), staleTime: 30_000 }) + + const rules = data?.rules || [] + const cols = [ + { title: '名称', dataIndex: 'name', width: 100 }, + { title: '说明', dataIndex: 'desc', width: 150 }, + { title: '窗口', dataIndex: 'ttl', width: 80 }, + { title: '上限', dataIndex: 'limit', width: 80, render: (v: number) => {v}次 }, + ] + + return ( +
+
+ <ThunderboltOutlined /> 限流管理 + +
+ +
+ + + +
+ + ) +}