import { useQuery } from '@tanstack/react-query'; import { useMemo } from 'react'; import { HttpUtil } from '@/utils'; import { parseMsg } from '@/utils/zodValidate'; import { NodeListSchema } from '@/schemas/node'; import type { NodeRecord } from '@/schemas/node'; import { keys } from '@/api/queryKeys'; export type { NodeRecord }; export interface NodeTotals { total: number; online: number; offline: number; avgLatency: number; inbounds: number; clients: number; onlineClients: number; depleted: number; } async function fetchNodes(): Promise { const msg = await HttpUtil.get('/panel/api/nodes/list', undefined, { silent: true }); if (!msg?.success) throw new Error(msg?.msg || 'Failed to fetch nodes'); const validated = parseMsg(msg, NodeListSchema, 'nodes/list'); return Array.isArray(validated.obj) ? validated.obj : []; } export function useNodesQuery() { const query = useQuery({ queryKey: keys.nodes.list(), queryFn: fetchNodes, }); const nodes = useMemo(() => query.data ?? [], [query.data]); const totals = useMemo(() => { let online = 0; let offline = 0; let latencySum = 0; let latencyCount = 0; let inbounds = 0; let clients = 0; let onlineClients = 0; let depleted = 0; for (const n of nodes) { inbounds += n.inboundCount || 0; clients += n.clientCount || 0; onlineClients += n.onlineCount || 0; depleted += n.depletedCount || 0; if (!n.enable) continue; if (n.status === 'online') { online += 1; if (n.latencyMs && n.latencyMs > 0) { latencySum += n.latencyMs; latencyCount += 1; } } else if (n.status === 'offline') { offline += 1; } } return { total: nodes.length, online, offline, avgLatency: latencyCount > 0 ? Math.round(latencySum / latencyCount) : 0, inbounds, clients, onlineClients, depleted, }; }, [nodes]); return { nodes, totals, loading: query.isFetching, fetched: query.data !== undefined || query.isError, fetchError: query.error ? (query.error as Error).message : '', refetch: query.refetch, }; }