feat: M0-08 AI Gateway admin web page
Some checks failed
Deploy Admin Frontend / build-and-deploy (push) Has been cancelled

This commit is contained in:
WangDL 2026-05-23 09:39:53 +08:00
parent b90ac755c4
commit badf4258b1
4 changed files with 63 additions and 0 deletions

View File

@ -107,6 +107,10 @@ function App() {
path="config"
element={<PermissionGuard requiredRole="SUPER_ADMIN"><Suspense fallback={<PageLoading />}><ConfigPage /></Suspense></PermissionGuard>}
/>
<Route
path="ai-gateway"
element={<PermissionGuard requiredRole="SUPER_ADMIN"><Suspense fallback={<PageLoading />}><AiGatewayPage /></Suspense></PermissionGuard>}
/>
<Route
path="metrics"
element={<PermissionGuard requiredRole="SUPER_ADMIN"><Suspense fallback={<PageLoading />}><MetricsPage /></Suspense></PermissionGuard>}

View File

@ -31,6 +31,7 @@ export const adminMenuItems: AdminMenuItem[] = [
{ path: '/git', name: '代码仓库', icon: <CodeOutlined /> },
{ path: '/ops', name: '系统运维', icon: <CloudServerOutlined />, requiredRole: 'SUPER_ADMIN', children: [
{ path: '/metrics', name: '接口监控' },
{ path: '/ai-gateway', name: 'AI Gateway' },
{ path: '/servers', name: '服务器' },
{ path: '/events', name: '事件队列' },
{ path: '/config', name: '配置管理' },

View File

@ -26,6 +26,7 @@ const breadcrumbMap: Record<string, string> = {
'/servers': '服务器运维',
'/config': '配置管理',
'/metrics': '接口监控',
'/ai-gateway': 'AI Gateway',
'/safety': '内容安全',
'/events': '事件队列',
'/audit': '审计日志',

57
src/pages/AiGateway.tsx Normal file
View File

@ -0,0 +1,57 @@
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Card, Row, Col, Statistic, Table, Tag, Button, Typography } from 'antd'
import { ReloadOutlined, CloudOutlined } from '@ant-design/icons'
import { api } from '@/services/http-client'
const { Title, Text } = Typography
export default function AiGatewayPage() {
const qc = useQueryClient()
const { data } = useQuery({ queryKey: ['ai-gateway', 'status'], queryFn: (): Promise<any> => api.get('/admin-api/ai-gateway/status'), staleTime: 30_000 })
const tierCols = [
{ title: '级别', dataIndex: 'name', width: 80 },
{ title: '主模型', dataIndex: 'provider', width: 120 },
{ title: '模型', dataIndex: 'model', width: 180 },
{ title: '备用', dataIndex: 'fallback', width: 180, render: (v: string) => v || '-' },
]
const tiers = data?.tiers ? [
{ name: 'cheap', ...data.tiers.cheap, fallback: data.tiers.cheap.fallback?.model || '-' },
{ name: 'primary', ...data.tiers.primary, fallback: data.tiers.primary.fallback?.model || data.tiers.primary.fallback || '-' },
{ name: 'strong', ...data.tiers.strong, fallback: data.tiers.strong.fallback?.model || '-' },
] : []
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
<Title level={5} style={{ margin: 0 }}><CloudOutlined /> AI Gateway</Title>
<Button icon={<ReloadOutlined />} onClick={() => qc.invalidateQueries({ queryKey: ['ai-gateway'] })}></Button>
</div>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={6}><Card size="small"><Statistic title="Provider" value={data?.providers?.length || 0} suffix="个" /></Card></Col>
<Col span={6}><Card size="small"><Statistic title="Prompt 模板" value={data?.prompts?.length || 0} suffix="个" /></Card></Col>
<Col span={6}><Card size="small"><Statistic title="路由级别" value={3} suffix="层" /></Card></Col>
<Col span={6}><Card size="small"><Statistic title="重试次数" value={data?.retry?.primary || 3} suffix="次" /></Card></Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<Card size="small" title="Provider 列表">
<Table dataSource={(data?.providers || []).map((p: string) => ({ name: p }))} columns={[{ title: '名称', dataIndex: 'name', render: (v: string) => <Tag color={v==='deepseek'?'blue':v==='minimax'?'purple':'green'}>{v}</Tag> }]} rowKey="name" pagination={false} size="small" />
</Card>
</Col>
<Col span={12}>
<Card size="small" title="模型路由">
<Table dataSource={tiers} columns={tierCols} rowKey="name" pagination={false} size="small" />
</Card>
</Col>
</Row>
<Card size="small" title="Prompt 模板" style={{ marginTop: 16 }}>
<Table dataSource={(data?.prompts || []).map((p: string) => ({ name: p }))} columns={[{ title: '名称', dataIndex: 'name', render: (v: string) => <Text code>{v}</Text> }]} rowKey="name" pagination={false} size="small" />
</Card>
</div>
)
}