diff --git a/src/App.tsx b/src/App.tsx index c611988..e25a22a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,7 @@ import PageLoading from './components/PageLoading' import AdminLayout from './layouts/AdminLayout' const Login = lazy(() => import('./pages/Login')) +const ServersPage = lazy(() => import("./pages/Servers")) const AuditLogPage = lazy(() => import("./pages/AuditLog")) const Dashboard = lazy(() => import('./pages/Dashboard')) const UserManagement = lazy(() => import('./pages/UserManagement')) @@ -80,6 +81,14 @@ function App() { } /> + + }> + + } + /> }, { path: '/files', name: '文件与 COS', icon: }, { path: '/settings', name: '系统配置', icon: , requiredRole: 'ADMIN' }, + { path: '/servers', name: '服务器运维', icon: , requiredRole: 'SUPER_ADMIN' }, { path: '/audit', name: '审计日志', icon: , requiredRole: 'ADMIN' }, ] diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx index 9dd2c8e..3c60799 100644 --- a/src/layouts/AdminLayout.tsx +++ b/src/layouts/AdminLayout.tsx @@ -21,6 +21,7 @@ const breadcrumbMap: Record = { '/ai-costs': 'AI 调用与成本', '/files': '文件与 COS', '/settings': '系统配置', + '/servers': '服务器运维', '/audit': '审计日志', } diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index b3ef42a..f1483a3 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -6,11 +6,14 @@ import * as echarts from 'echarts/core' import { LineChart, BarChart } from 'echarts/charts' import { GridComponent, TooltipComponent, TitleComponent, LegendComponent } from 'echarts/components' import { CanvasRenderer } from 'echarts/renderers' -import { UserOutlined, BookOutlined, CloudOutlined, FileOutlined } from '@ant-design/icons' +import { UserOutlined, BookOutlined, CloudOutlined, FileOutlined, ClusterOutlined } from '@ant-design/icons' import dayjs from 'dayjs' import MetricCard from '@/components/MetricCard' +import { Progress, Space } from 'antd' +const DText = Typography.Text import EChartsChartContainer from '@/components/EChartsChartContainer' import { getDashboardStats } from '@/services/admin-api' +import { getServerMetrics } from '@/services/server-api' echarts.use([LineChart, BarChart, GridComponent, TooltipComponent, TitleComponent, LegendComponent, CanvasRenderer]) @@ -27,6 +30,12 @@ export default function Dashboard() { staleTime: 60_000, }) + const { data: serverData } = useQuery({ + queryKey: ['servers', 'metrics'], + queryFn: getServerMetrics, + refetchInterval: 10_000, + }) + const userTrendOption = useMemo(() => ({ grid: { top: 20, right: 20, bottom: 20, left: 40 }, tooltip: { trigger: 'axis' as const }, @@ -56,6 +65,25 @@ export default function Dashboard() { + + {serverData?.servers?.map(s => ( + +
+ {s.name}{s.network.ip} + + + CPU {s.cpu.usagePercent}% + 80 ? '#ff4d4f' : '#1677ff'} showInfo={false} /> + + + 内存 {s.memory.percent}% + 80 ? '#ff4d4f' : '#52c41a'} showInfo={false} /> + + +
+ + ))} +
) } diff --git a/src/pages/Servers.tsx b/src/pages/Servers.tsx new file mode 100644 index 0000000..3b61870 --- /dev/null +++ b/src/pages/Servers.tsx @@ -0,0 +1,75 @@ +import { useQuery } from '@tanstack/react-query' +import { Card, Row, Col, Progress, Table, Tag, Typography, theme, Space } from 'antd' +import { CloudServerOutlined, DashboardOutlined } from '@ant-design/icons' +import { getServerMetrics, type ServerInfo } from '@/services/server-api' + +const { Text, Title } = Typography + +function ServerCard({ server }: { server: ServerInfo }) { + theme.useToken() + const cpuColor = server.cpu.usagePercent > 80 ? 'red' : server.cpu.usagePercent > 50 ? 'orange' : 'green' + const memColor = server.memory.percent > 80 ? 'red' : server.memory.percent > 50 ? 'orange' : 'green' + + return ( + {server.name}{server.role}} + extra={{server.network.ip}} + style={{ height: '100%' }}> + + + CPU ({server.cpu.cores}核) + + {server.cpu.model?.slice(0, 40)} + + + 内存 + + {server.memory.used}/{server.memory.total} + + + 磁盘 + 80 ? 'red' : 'blue'} size="small" /> + {server.disk.used}/{server.disk.total} + + + 运行时间 +
{server.uptime}
+ +
+ + + ) +} + +export default function ServersPage() { + const { data } = useQuery({ + queryKey: ['servers', 'metrics'], + queryFn: getServerMetrics, + refetchInterval: 10_000, + }) + + return ( +
+ <DashboardOutlined /> 服务器运维 + + {(data?.servers || []).map(s => ( +
+ + + ))} + + + ) +} diff --git a/src/services/server-api.ts b/src/services/server-api.ts new file mode 100644 index 0000000..7cae6a9 --- /dev/null +++ b/src/services/server-api.ts @@ -0,0 +1,17 @@ +import { api } from './http-client' + +export interface ServerInfo { + name: string + role: string + hostname: string + cpu: { model: string; cores: number; usagePercent: number; loadAvg: number[] } + memory: { total: string; used: string; free: string; percent: number } + disk: { total: string; used: string; free: string; percent: number } + uptime: string + network: { ip: string } + processes: { pid: number; cpu: string; mem: string; command: string }[] +} + +export function getServerMetrics(): Promise<{ servers: ServerInfo[] }> { + return api.get('/admin-api/servers/metrics') +}