/* eslint-disable @typescript-eslint/no-explicit-any */ import { useState, useEffect, useCallback } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { RefreshCw, Server, Activity, ArrowLeft, Container, Network, Info, AlertTriangle, CheckCircle2, } from 'lucide-react'; import { kubernetesApi } from '@/api'; import { PageShell } from '@/components/layout/PageShell'; export default function NodeDetailPage() { const { clusterCode, nodeName } = useParams<{ clusterCode: string; nodeName: string }>(); const navigate = useNavigate(); const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const loadData = useCallback(async () => { if (!clusterCode || !nodeName) return; setLoading(true); try { const res: Record = await kubernetesApi.getNodeDetail(clusterCode, nodeName); setData(res); } catch { setData(null); } finally { setLoading(false); } }, [clusterCode, nodeName]); useEffect(() => { loadData(); }, [loadData]); const _formatNum = ( v: any, suffix = '') => { if (v === undefined || v === null || v === '') return '-'; const n = typeof v === 'number' ? v : parseFloat(v); return isNaN(n) ? '-' : `${n.toLocaleString()}${suffix}`; }; // Resource bar component const ResourceBar = ({ label, used, total, unit, pct, color }: { label: string; used: string; total: string; unit: string; pct: number; color: string }) => { const safePct = Math.min(Math.max(pct || 0, 0), 100); const barColor = safePct > 80 ? 'bg-red-500' : safePct > 60 ? 'bg-amber-500' : 'bg-emerald-500'; return (
{label} {used} / {total} {unit}
{safePct.toFixed(0)}%
); }; const conditionColor = (type: string, status: string) => { if (status === 'True') { if (type === 'Ready') return 'bg-emerald-500/20 dg-text-green'; // Other True conditions (DiskPressure, MemoryPressure, PIDPressure, NetworkUnavailable) are BAD return 'bg-red-500/20 dg-text-red'; } return 'bg-[var(--dg-bg-active)]/20 dg-text-secondary'; }; const taintColor = (effect: string) => { if (effect === 'NoSchedule') return 'bg-red-500/20 dg-text-red border-red-500/30'; if (effect === 'NoExecute') return 'bg-red-600/20 dg-text-red border-red-600/30'; if (effect === 'PreferNoSchedule') return 'bg-amber-500/20 dg-text-yellow border-amber-500/30'; return 'bg-[var(--dg-bg-active)]/20 dg-text-secondary border-[var(--dg-border)]'; }; const nodeSubtitle = [ clusterCode, data?.internal_ip, data?.hostname, ].filter(Boolean).join(' · '); return ( } loading={loading && !data} loadingLabel="加载节点详情..." actions={( <> )} > {data && (
{data?.role || 'worker'} {data?.status || 'Unknown'}
)} {!loading && !data ? ( 无法加载节点详情 ) : data ? ( <> {/* Row 1: System Info + Basic Info */}
{/* System Info */}

系统信息

容器运行时 {data.container_runtime || '-'}
Kubelet {data.kubelet_version || '-'}
操作系统 {data.os_image || '-'}
内核版本 {data.kernel_version || '-'}
架构 {data.architecture || '-'}
创建时间 {data.creation_time ? new Date(data.creation_time).toLocaleString('zh-CN') : '-'}
Pod CIDR {data.pod_cidr || '-'}
{/* Resource Allocation */}

资源分配率

{/* CPU allocation (requested vs allocatable) */} {/* CPU real usage (from metrics) */} {data.cpu_usage_m && ( )} {/* Memory allocation */} {/* Memory real usage */} {data.mem_usage_mi && ( )} {/* Pod allocation */}
{/* Capacity vs Allocatable raw */}
CPU 容量 / 可分配 {data.cpu_capacity} / {data.cpu_allocatable} 核
内存容量 / 可分配 {data.mem_capacity_mb} / {data.mem_allocatable_mb} Mi
Pod 容量 / 可分配 {data.pod_capacity} / {data.pod_allocatable}
{/* Row 2: Conditions + Taints */}
{/* Conditions */}

节点条件

{(!data.conditions || data.conditions.length === 0) ? (

无条件数据

) : (
{data.conditions.map(( cond: any, i: number) => (
{cond.status === 'True' ? : null} {cond.type}
{cond.reason && {cond.reason}} {cond.message && {cond.message}}
))}
)}
{/* Taints */}

污点 (Taints)

{(!data.taints || data.taints.length === 0) ? (

无污点

) : (
{data.taints.map(( t: any, i: number) => (
{t.effect} {t.key} {t.value && = {t.value}}
))}
)}
{/* Row 3: Labels */} {data.labels && data.labels.length > 0 && (

标签 ({data.labels.length})

{data.labels.map(( l: any, i: number) => (
{l.key} : {l.value}
))}
)} {/* Row 4: Pods on this node */}

节点上的 Pod {data.pods?.length || 0}

{(!data.pods || data.pods.length === 0) ? (

该节点上暂无 Pod

) : ( 命名空间 名称 状态 IP CPU 请求 内存请求 重启次数 {data.pods.map(( p: any, i: number) => ( {p.namespace} {p.name} {p.status} {p.ip || '-'} {p.cpu_request_m > 0 ? `${p.cpu_request_m}m` : '-'} {p.mem_request_mi > 0 ? `${p.mem_request_mi}Mi` : '-'} {p.restart_count > 0 ? ( {p.restart_count} ) : ( 0 )} ))}
)}
) : null}
); }