'use client' import { formatHumanBytes, formatHumanSpeed, formatTransferProgressSummary } from '@/lib/human-readable-transfer' type UploadTask = { uploadId?: string fileName?: string username?: string materialType?: string progress?: number currentSpeed?: number uploadedBytes?: number fileSize?: number isActive?: boolean inactiveMs?: number } type TranscodeTask = { materialId?: string fileName?: string materialType?: string authorName?: string stage?: string | null progress?: number | null queuePosition?: number } export type SystemPerformanceData = { cpu?: { usage: number; loadAverage: number[]; cores?: number } memory?: { used: number total: number percentage: number usedBytes?: number totalBytes?: number source?: 'container' | 'host' hostUsedBytes?: number hostTotalBytes?: number hostPercentage?: number } network?: { uploadSpeed: number; downloadSpeed: number } uploadQueue?: { total: number; processing: UploadTask[]; waiting: UploadTask[] } transcodeQueue?: { total: number; maxConcurrent: number; processing: TranscodeTask[]; waiting: TranscodeTask[] } } const STAGE_LABEL: Record = { preview: '预览视频', clip: '片段生成', cover: '封面提取', } function formatAgo(ms: number): string { if (!Number.isFinite(ms) || ms < 0) return '--' const sec = Math.floor(ms / 1000) if (sec < 60) return `${sec} 秒` const min = Math.floor(sec / 60) if (min < 60) return `${min} 分钟` const hr = Math.floor(min / 60) return `${hr} 小时 ${min % 60} 分钟` } function Meter({ value, warn = false }: { value: number; warn?: boolean }) { const width = Math.min(Math.max(value, 0), 100) return (
) } function QueueSummary({ processing, waiting, capacity, }: { processing: number waiting: number capacity?: number }) { if (processing === 0 && waiting === 0) { return 空闲 } const processingText = typeof capacity === 'number' && capacity > 0 ? `进行中 ${processing} / ${capacity}` : `进行中 ${processing}` return ( {processingText} {waiting > 0 ? ` · 停住 ${waiting}` : ''} ) } export default function SystemPerformanceCard({ data, loading, error, }: { data: SystemPerformanceData | null loading: boolean error: string | null }) { const cpu = data?.cpu const memory = data?.memory const memoryBytes = typeof memory?.usedBytes === 'number' && typeof memory?.totalBytes === 'number' ? { used: memory.usedBytes, total: memory.totalBytes } : memory ? { used: memory.used * 1024 * 1024, total: memory.total * 1024 * 1024 } : null const memoryWarn = (memory?.percentage ?? 0) >= 85 const uploadProcessing = data?.uploadQueue?.processing || [] const uploadWaiting = data?.uploadQueue?.waiting || [] const transcodeProcessing = data?.transcodeQueue?.processing || [] const transcodeWaiting = data?.transcodeQueue?.waiting || [] return (
系统性能 {error ? error : !data && loading ? '正在读取' : '应用容器此刻'}
CPU {cpu ? `${cpu.usage}%` : '--'}
{cpu?.cores ? `${cpu.cores} 核 · 1/5/15 分钟负载` : '1/5/15 分钟负载'} {cpu?.loadAverage?.length ? cpu.loadAverage.map((value) => value.toFixed(2)).join(' / ') : '--'}
{memory?.source === 'host' ? '内存 · 整机' : '内存'} {memoryBytes ? `${formatHumanBytes(memoryBytes.used)} / ${formatHumanBytes(memoryBytes.total)}(${memory?.percentage}%)` : '--'}
{memory?.source === 'container' && typeof memory.hostPercentage === 'number' ? (
整台机器大约用了 {memory.hostPercentage}% {typeof memory.hostUsedBytes === 'number' && typeof memory.hostTotalBytes === 'number' ? `(${formatHumanBytes(memory.hostUsedBytes)} / ${formatHumanBytes(memory.hostTotalBytes)})` : ''} ,主要是存储机自己在用
) : null} {memoryWarn ? (
这个应用自己的内存已经偏高,继续堆任务容易变慢
) : null}
进出流量 本应用,不是整台机器
进来(别人上传) {data?.network ? formatHumanSpeed(data.network.uploadSpeed) : '--'}
出去(别人下载) {data?.network ? formatHumanSpeed(data.network.downloadSpeed) : '--'}
上传
{uploadProcessing.map((task, index) => { const uploadedBytes = task.uploadedBytes || (task.fileSize || 0) * ((task.progress || 0) / 100) const speedText = task.currentSpeed ? formatTransferProgressSummary({ speedBytesPerSecond: task.currentSpeed, loadedBytes: uploadedBytes, totalBytes: task.fileSize, }) : '速度还在统计' return (
{task.fileName || '未命名文件'}
{task.username || '未知用户'} {task.materialType || '未知类型'}
{task.progress ?? 0}% · {speedText}
) })} {uploadWaiting.map((task, index) => (
{task.fileName || '未命名文件'}
{task.username || '未知用户'} {task.materialType || '未知类型'}
已经停住 {formatAgo(task.inactiveMs || 0)}
))}
转码
{transcodeProcessing.map((task, index) => (
{task.fileName || '未命名文件'}
{task.materialType || '未知类型'} {task.stage ? ` · ${STAGE_LABEL[task.stage] || task.stage}` : ''} {task.progress ?? 0}%
上传人 {task.authorName || '未知用户'}
))} {transcodeWaiting.map((task, index) => (
{task.fileName || '未命名文件'}
{task.materialType || '未知类型'} 排队 #{task.queuePosition || index + 1}
上传人 {task.authorName || '未知用户'}
))}
) }