'use client' import React from 'react' import { useTranslation } from 'react-i18next' import { useQuery } from '@tanstack/react-query' import { getWorkerStatus, getWorkerIngress } from '@/api/worker' import { getProxyConfig } from '@/api/proxy' import { useStore } from '@nanostores/react' import { $proxyTableRefetchTrigger } from '@/store/refetch-trigger' import { Client, ProxyConfig } from '@/lib/pb/common' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' import { Badge } from '@/components/ui/badge' import { Cpu, Network } from 'lucide-react' interface WorkerStatusProps { workerId: string clients?: Client[] compact?: boolean } export function WorkerStatus({ workerId, clients = [], compact = false }: WorkerStatusProps) { const { t } = useTranslation() const refetchTrigger = useStore($proxyTableRefetchTrigger) // 获取 Worker 状态 const { data: statusResp } = useQuery({ queryKey: ['workerStatus', workerId], queryFn: () => getWorkerStatus({ workerId }), enabled: !!workerId, refetchInterval: 10000, }) // 获取 Worker Ingress const { data: ingressResp } = useQuery({ queryKey: ['getWorkerIngress', workerId], queryFn: () => getWorkerIngress({ workerId }), enabled: !!workerId, }) // 状态统计 const clientStatuses = statusResp?.workerStatus || {} const deployedClients = clients const totalClients = deployedClients.length const runningClients = Object.values(clientStatuses).filter((s) => s === 'running').length const Clients = Object.values(clientStatuses).filter((s) => s === '').length const stoppedClients = Object.values(clientStatuses).filter((s) => s === 'stopped').length const ingresses = ingressResp?.proxyConfigs || [] const totalIngresses = ingresses.length // 针对每个 ingress 再次拉取状态 const ingressStatuses = useQuery({ queryKey: ['getIngressStatuses', workerId, ingresses.map((i) => i.id).join(','), refetchTrigger], queryFn: async () => { const statuses: Record = {} await Promise.all( ingresses.map(async (i) => { try { const ps = await getProxyConfig({ clientId: i.clientId, serverId: i.serverId, name: i.name, }) statuses[i.id || ''] = ps?.workingStatus?.status || 'unknown' } catch { statuses[i.id || ''] = '' } }), ) return statuses }, enabled: ingresses.length > 0, refetchInterval: 10000, }) const runningIngresses = Object.values(ingressStatuses.data || {}).filter((s) => s === 'running').length const Ingresses = Object.values(ingressStatuses.data || {}).filter((s) => ['', 'start', 'check failed'].includes(s), ).length // Overall 状态 const getOverallStatus = () => { if (totalClients === 0 && totalIngresses === 0) { return { variant: 'outline' as const, text: t('worker.status.no_resources'), color: 'bg-gray-100 text-gray-700' } } if ((totalClients > 0 && runningClients === 0) || (totalIngresses > 0 && runningIngresses === 0)) { return { variant: 'destructive' as const, text: t('worker.status.unusable'), color: 'bg-red-500 text-white' } } if (Clients > 0 || Ingresses > 0) { return { variant: 'warning' as const, text: t('worker.status.unhealthy'), color: 'bg-amber-500 text-white' } } if (runningClients === totalClients && runningIngresses === totalIngresses) { return { variant: 'default' as const, text: t('worker.status.healthy'), color: 'bg-green-500 text-white' } } return { variant: 'secondary' as const, text: t('worker.status.degraded'), color: 'bg-orange-500 text-white' } } const { text: overallText, color: overallColor } = getOverallStatus() // per-client indicators const renderClientIndicators = () => { if (totalClients === 0) return null const showList = deployedClients.slice(0, 3) return (
{showList.map((client) => { const status = clientStatuses[client.id || ''] || 'unknown' const bg = status === 'running' ? 'bg-green-500' : status === '' ? 'bg-red-500' : 'bg-gray-300' return (

{client.id}

{t('worker.status.clients')}: {status}
) })}
{totalClients > 3 && +{totalClients - 3}}
) } // per-ingress indicators const renderIngressIndicators = () => { if (totalIngresses === 0) return null const showList = ingresses.slice(0, 3) return (
{showList.map((ing) => { const status = ingressStatuses.data?.[ing.id || ''] || 'unknown' const bg = status === 'running' ? 'bg-green-500' : ['', 'start', 'check failed'].includes(status) ? 'bg-red-500' : 'bg-gray-300' return (

{ing.name}

{t('worker.status.ingresses')}: {status}
) })}
{totalIngresses > 3 && +{totalIngresses - 3}}
) } // Compact 模式仍旧整体 hover if (compact) { return (

{overallText}

{t('worker.status.clients')}: {runningClients}/{totalClients}
{t('worker.status.ingresses')}: {runningIngresses}/{totalIngresses}
) } // 默认模式:拆分整体与细节 hover return (
{renderIngressIndicators()} {renderClientIndicators()} {/* 只在 Badge 上展示总体状态 */} {overallText}

{overallText}

{t('worker.status.running')}: {runningClients}/{totalClients}
{t('worker.status_text')}: {Clients}/{totalClients}
{t('worker.status.running')}: {runningIngresses}/{totalIngresses}
{t('worker.status_text')}: {Ingresses}/{totalIngresses}
) }