From 8da983b0ebcab8ea9edc4714425d7206f7dea9ce Mon Sep 17 00:00:00 2001 From: wangdl Date: Fri, 19 Jun 2026 22:03:08 +0800 Subject: [PATCH] feat: add Worker status section to Servers page - New WorkerInfo API type + getWorkerStatus() in server-api.ts - WorkerStatusSection component: online/stale/offline cards with instanceId, version, hostname, queues, heartbeat age - Auto-refresh every 15s - Register /servers route (SUPER_ADMIN only) Co-Authored-By: Claude Opus 4.7 --- src/constants/table.ts | 13 +++++++++ src/pages/Servers.tsx | 59 +++++++++++++++++++++++++++++++++++++- src/routes/index.tsx | 2 ++ src/services/server-api.ts | 14 +++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/constants/table.ts diff --git a/src/constants/table.ts b/src/constants/table.ts new file mode 100644 index 0000000..7bbfc10 --- /dev/null +++ b/src/constants/table.ts @@ -0,0 +1,13 @@ +import type { TablePaginationConfig } from 'antd' + +/** 全局默认分页配置 */ +export const defaultPagination: TablePaginationConfig = { + defaultPageSize: 20, + showSizeChanger: true, + pageSizeOptions: ['10', '20', '50', '100'], +} + +/** 合并默认分页配置 */ +export function pagination(custom?: TablePaginationConfig): TablePaginationConfig { + return { ...defaultPagination, ...custom } +} diff --git a/src/pages/Servers.tsx b/src/pages/Servers.tsx index babe4d9..965c4d6 100644 --- a/src/pages/Servers.tsx +++ b/src/pages/Servers.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { useQuery, useQueryClient } from '@tanstack/react-query' import { Card, Row, Col, Progress, Table, Tag, Typography, Button, Space, Tooltip, App } from 'antd' import { CloudServerOutlined, ReloadOutlined, CopyOutlined, GlobalOutlined } from '@ant-design/icons' -import { getServerMetrics, getServerHealth, type ServerInfo, type ProcessInfo, type ServerHealth } from '@/services/server-api' +import { getServerMetrics, getServerHealth, getWorkerStatus, type ServerInfo, type ProcessInfo, type ServerHealth } from '@/services/server-api' const { Text, Title } = Typography @@ -86,6 +86,60 @@ function ServerCard({ server }: { server: ServerInfo }) { ) } +function WorkerStatusSection() { + const { data, isLoading } = useQuery({ + queryKey: ['servers', 'workers'], + queryFn: getWorkerStatus, + staleTime: 15_000, + refetchInterval: 15_000, + }) + + const workers = data?.workers ?? [] + + const statusColor = (s: string) => s === 'online' ? 'green' : s === 'stale' ? 'orange' : 'red' + const statusLabel = (s: string) => s === 'online' ? '在线' : s === 'stale' ? '延迟' : '离线' + + return ( +
+ + 🟢 Worker 实例 + <Tag style={{ marginLeft: 8 }}>{workers.length} 个实例</Tag> + + {workers.length === 0 && !isLoading ? ( + 暂无 Worker 在线(Heartbeat 未检测到) + ) : ( + + {workers.map(w => ( + + + {statusLabel(w.status)} + {w.instanceId?.slice(0, 40)}... + + }> + + 版本
{w.appVersion} + 主机
{w.hostname} + 启动时间
{w.startedAt ? new Date(w.startedAt).toLocaleString() : '-'} + 最后心跳
{w.lastHeartbeatAgo}s 前 +
+
+ 队列 +
+ + {w.queues?.map(q => {q})} + +
+
+
+ + ))} +
+ )} +
+ ) +} + function ServersContent() { const qc = useQueryClient() const [refreshing, setRefreshing] = useState(false) @@ -122,6 +176,9 @@ function ServersContent() { ))} + {/* Worker Status Section */} + + {/* Health Check Section */} {health?.length ? (
diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 485dd47..94ea49a 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -15,6 +15,7 @@ const AnomalyPage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ const UserTimelinePage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.UserTimelinePage }))) const UserDiagnosePage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.UserDiagnosePage }))) const MaterialDiagnosePage = lazy(() => import('@/pages/learning/DataPages').then(m => ({ default: m.MaterialDiagnosePage }))) +const ServersPage = lazy(() => import('@/pages/Servers')) const KnowledgeSourcesPage = lazy(() => import('@/pages/KnowledgeSources')) const LearningReplay = lazy(() => import('@/pages/learning/ReplayPage')) @@ -37,6 +38,7 @@ export const routeConfig: RouteConfig[] = [ { path: '/ai-costs', title: 'AI 调用与成本', element: UserManagement }, { path: '/files', title: '文件与 COS', element: UserManagement }, { path: '/settings', title: '系统配置', element: UserManagement, requiredRole: 'ADMIN' }, + { path: '/servers', title: '服务器运维', element: ServersPage, requiredRole: 'SUPER_ADMIN' }, { path: '/audit', title: '审计日志', element: UserManagement, requiredRole: 'ADMIN' }, // ── Learning Info ── { path: '/learning', title: '学习 Dashboard', element: LearningDashboard }, diff --git a/src/services/server-api.ts b/src/services/server-api.ts index 71349eb..63baa71 100644 --- a/src/services/server-api.ts +++ b/src/services/server-api.ts @@ -26,3 +26,17 @@ export function getServerMetrics(): Promise<{ servers: ServerInfo[] }> { export function getServerHealth(): Promise { return api.get('/admin-api/servers/health') } + +export interface WorkerInfo { + instanceId: string + appVersion: string + startedAt: string + queues: string[] + hostname: string + lastHeartbeatAgo: number + status: 'online' | 'stale' | 'offline' +} + +export function getWorkerStatus(): Promise<{ workers: WorkerInfo[] }> { + return api.get('/admin-api/servers/workers') +}