import { Activity, AlertTriangle, Bot, CakeSlice, Candy, CircleGauge, Info, MoonStar, RotateCw, ServerCog, Sparkles, Workflow } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { platformApiClient } from "../api/client"; import type { AiProviderResponse, JobResponse, PlatformResourceUsageResponse, RunEndpointResponse, ServerInstanceResponse, ServerMetricsResponse } from "../api/types"; import { UsageMeter } from "../components/OperationControls"; import { EmptyState, ErrorState, LoadingState } from "../components/StateViews"; import { jobBuckets, moduleFreshnessLabel, summarizeEndpointOperations, type OperationsModuleState } from "../contracts/operationsConsole"; import { jobCapabilityLabel } from "../contracts/jobPresentation"; import type { PageComponentProps } from "../contracts/page"; import type { GameTypeDistributionEntry, PlatformOverviewSignal } from "../contracts/workspace"; import { serverIsOnline } from "../contracts/workspace"; import { cx } from "../utils/classes"; interface OverviewData { instances: ServerInstanceResponse[]; endpoints: RunEndpointResponse[]; jobs: JobResponse[]; } export interface HomePageInitialState { core?: OperationsModuleState; metrics?: OperationsModuleState; usage?: OperationsModuleState; providers?: OperationsModuleState; } interface HomePageProps extends PageComponentProps { initialState?: HomePageInitialState; } export function HomePage({ session, onNavigate, initialState }: HomePageProps) { const [core, setCore] = useState>(initialState?.core ?? { status: "loading" }); const [metrics, setMetrics] = useState>(initialState?.metrics ?? { status: "loading" }); const [usage, setUsage] = useState>(initialState?.usage ?? { status: "loading" }); const [providers, setProviders] = useState>(initialState?.providers ?? { status: "loading" }); const refreshCore = useCallback(async () => { setCore({ status: "loading" }); try { const [instances, endpoints, jobs] = await Promise.all([ platformApiClient.listServerInstances(), platformApiClient.listRunEndpoints(), platformApiClient.listJobs() ]); setCore({ status: "ready", data: { instances: instances.items, endpoints: endpoints.items, jobs: jobs.items }, refreshedAt: refreshedNow() }); } catch (error) { setCore({ status: "error", reason: errorMessage(error, "服务器、节点或任务加载失败"), diagnosticId: "overview-core" }); } }, []); const refreshMetrics = useCallback(async () => { setMetrics({ status: "loading" }); try { const response = await platformApiClient.listServerMetrics(); setMetrics({ status: "ready", data: response.items, refreshedAt: refreshedNow() }); } catch (error) { setMetrics({ status: "error", reason: errorMessage(error, "服务器指标加载失败"), diagnosticId: "overview-server-metrics" }); } }, []); const refreshUsage = useCallback(async () => { setUsage({ status: "loading" }); try { const response = await platformApiClient.getPlatformResourceUsage(); setUsage({ status: "ready", data: response, refreshedAt: refreshedNow() }); } catch (error) { setUsage({ status: "error", reason: errorMessage(error, "平台资源指标加载失败"), diagnosticId: "overview-platform-usage" }); } }, []); const refreshProviders = useCallback(async () => { setProviders({ status: "loading" }); try { const response = await platformApiClient.listAiProviders(); setProviders({ status: "ready", data: response.items, refreshedAt: refreshedNow() }); } catch (error) { setProviders({ status: "error", reason: errorMessage(error, "AI 提供商状态加载失败"), diagnosticId: "overview-ai-providers" }); } }, []); const refreshAll = useCallback(() => { void refreshCore(); void refreshMetrics(); void refreshUsage(); void refreshProviders(); }, [refreshCore, refreshMetrics, refreshProviders, refreshUsage]); useEffect(() => { if (initialState) { return; } refreshAll(); }, [initialState, refreshAll]); const distribution = useMemo(() => { if (core.status !== "ready") { return []; } const counts = new Map(); for (const instance of core.data.instances) { counts.set(instance.pluginId, (counts.get(instance.pluginId) ?? 0) + 1); } return [...counts.entries()].map(([serverType, count]) => ({ serverType, label: serverType, count })); }, [core]); const overviewSignals = useMemo(() => buildOverviewSignals(core, providers), [core, providers]); const jobs = core.status === "ready" ? jobBuckets(core.data.jobs) : null; const endpointSummary = core.status === "ready" ? summarizeEndpointOperations(core.data.endpoints) : null; const onlineCount = core.status === "ready" ? core.data.instances.filter((item) => serverIsOnline(item.state)).length : 0; const offlineCount = core.status === "ready" ? core.data.instances.length - onlineCount : 0; const activeProviders = providers.status === "ready" ? providers.data.filter((item) => item.status === "active").length : 0; const errorProviders = providers.status === "ready" ? providers.data.filter((item) => item.status === "error").length : 0; const moduleFailureCount = [core, metrics, usage, providers].filter((module) => module.status === "error").length; const canManageServers = session.capabilities.includes("servers.manage"); const metricAverages = useMemo(() => { if (usage.status === "ready") { return { cpu: usage.data.cpuPercent, memory: usage.data.memoryPercent, disk: usage.data.diskPercent, source: "平台指标", refreshedAt: usage.refreshedAt }; } if (metrics.status === "ready" && metrics.data.length > 0) { const average = (values: Array) => { const known = values.filter((value): value is number => typeof value === "number"); return known.length > 0 ? known.reduce((sum, value) => sum + value, 0) / known.length : undefined; }; return { cpu: average(metrics.data.map((item) => item.cpuPercent)), memory: average(metrics.data.map((item) => item.memoryPercent)), disk: average(metrics.data.map((item) => item.diskPercent)), source: "服务器指标均值", refreshedAt: metrics.refreshedAt }; } return { cpu: undefined, memory: undefined, disk: undefined, source: metrics.status === "error" || usage.status === "error" ? "指标不可用" : "等待指标", refreshedAt: undefined }; }, [usage, metrics]); return (

运营指挥

平台概览

{moduleFailureCount > 0 ? `${moduleFailureCount} 个模块不可用` : core.status === "ready" ? "运营数据已同步" : "正在同步"}
{core.status === "loading" && } {core.status === "error" && void refreshCore()} />} {core.status === "ready" && core.data.instances.length === 0 && ( } title="还没有服务器实例" description={canManageServers ? "平台尚未创建服务器。先确认运行节点在线,再创建第一个实例。" : "当前账号可查看概览,但没有创建服务器的权限。"} actionLabel="前往服务器管理" onAction={() => onNavigate("servers")} /> )} {core.status === "ready" && (
在线服务器 {onlineCount}

共 {core.data.instances.length} 个实例

0 ? "metric-tone-warning" : "metric-tone-neutral")}> 离线 / 未运行 {offlineCount}

{core.data.instances.filter((item) => item.state === "failed").length} 个异常

0 ? "metric-tone-warning" : "metric-tone-neutral")}> 运行节点 {endpointSummary?.online ?? 0} 在线

{endpointSummary ? `${endpointSummary.degraded} 降级,${endpointSummary.offline} 离线` : "节点状态不可用"}

0 ? "metric-tone-warning" : "metric-tone-success")}> AI 提供商 {providers.status === "ready" ? `${activeProviders} 可用` : providers.status === "loading" ? "加载中" : "不可用"}

{providers.status === "error" ? "提供商状态接口失败" : errorProviders > 0 ? `${errorProviders} 个连接异常` : "连接状态正常"}

)}

任务脉冲

{moduleFreshnessLabel(core.status === "ready" ? core.refreshedAt : core.refreshedAt)}
{core.status === "loading" && } {core.status === "error" && void refreshCore()} compact />} {core.status === "ready" && jobs && ( <>
执行中
{jobs.active.length}
等待重试
{jobs.retrying.length}
近期失败
{jobs.failed.length}
{jobs.active.length === 0 && jobs.failed.length === 0 ? (

当前没有执行中或失败任务。

) : (
{[...jobs.active.slice(0, 3), ...jobs.failed.slice(0, 2)].map((job) => ( ))}
)} )}

运行节点

{core.status === "loading" && } {core.status === "error" && void refreshCore()} compact />} {core.status === "ready" && endpointSummary && ( <>
在线
{endpointSummary.online}
活跃任务
{endpointSummary.activeJobs}
排队
{endpointSummary.queuedJobs}
{core.data.endpoints.length === 0 ? (

Platform 未返回运行节点。

) : (
{core.data.endpoints.slice(0, 4).map((endpoint) => (
{endpoint.displayName}{endpoint.version} {endpointStatusLabel(endpoint.status)} {endpoint.capacity.runningJobs}/{endpoint.capacity.maxJobs} 运行
))}
)} )}

资源负载

{metricAverages.source}
{usage.status === "loading" && metrics.status === "loading" ? ( ) : usage.status === "error" && metrics.status === "error" ? ( { void refreshUsage(); void refreshMetrics(); }} compact /> ) : ( <>
{moduleFreshnessLabel(metricAverages.refreshedAt)} {(usage.status === "error" || metrics.status === "error") && ( )} )}

游戏类型分布

{core.status === "loading" ? ( ) : core.status === "error" ? ( void refreshCore()} compact /> ) : distribution.length === 0 ? ( ) : (
{distribution.map((entry) => {entry.label}:{entry.count} 个实例)}
)}

最近运营信号

{moduleFreshnessLabel(core.status === "ready" ? core.refreshedAt : providers.status === "ready" ? providers.refreshedAt : undefined)}
{core.status === "loading" && providers.status === "loading" && } {providers.status === "error" && void refreshProviders()} compact />} {overviewSignals.length === 0 && core.status === "ready" && providers.status === "ready" ? ( ) : (
{overviewSignals.map((signal) => ( ))}
)}
); } function buildOverviewSignals( core: OperationsModuleState, providers: OperationsModuleState ): PlatformOverviewSignal[] { const collected: PlatformOverviewSignal[] = []; if (core.status === "ready") { for (const instance of core.data.instances.filter((item) => item.state === "failed")) { collected.push({ id: `fault-${instance.id}`, kind: "fault", summary: `服务器 ${instance.name} 处于异常状态`, detail: `实例 ${instance.id} 需要排查`, targetPage: "servers", targetId: instance.id, tone: "error", at: instance.updatedAt }); } for (const job of jobBuckets(core.data.jobs).failed.slice(0, 5)) { collected.push({ id: `job-${job.id}`, kind: "job", summary: `任务 ${jobCapabilityLabel(job.capability)} 失败`, detail: job.serverInstanceId ? `服务器 ${job.serverInstanceId} 的任务执行失败` : "平台任务执行失败", targetPage: job.serverInstanceId ? "servers" : "maintenance", targetId: job.serverInstanceId, tone: "error", at: job.updatedAt }); } } if (providers.status === "ready") { for (const provider of providers.data.filter((item) => item.status === "error")) { collected.push({ id: `ai-${provider.id}`, kind: "aiProvider", summary: `AI 提供商 ${provider.name} 连接异常`, detail: "LLM 辅助暂不可用,请在提供商管理中检查已保存配置。", targetPage: "aiProviders", targetId: provider.id, tone: "warning", at: "" }); } } return collected.sort((left, right) => Date.parse(right.at || "") - Date.parse(left.at || "")).slice(0, 8); } function refreshedNow(): string { return new Date().toISOString(); } function errorMessage(error: unknown, fallback: string): string { return error instanceof Error ? error.message : fallback; } function jobStateLabel(state: JobResponse["state"]): string { const labels: Record = { queued: "排队", accepted: "已领取", running: "执行中", retrying: "等待重试", succeeded: "完成", failed: "失败", cancelled: "已取消" }; return labels[state]; } function endpointStatusLabel(status: RunEndpointResponse["status"]): string { const labels: Record = { online: "在线", offline: "离线", degraded: "降级", disabled: "停用" }; return labels[status]; }