import React, { createContext, useContext, useState, useEffect, useCallback, useRef, useMemo, } from "react"; import { getAllServerStatuses, getSSHHosts } from "@/main-axios"; import { DEFAULT_STATS_CONFIG } from "@/types/stats-widgets"; type StatusValue = "online" | "offline" | "degraded"; interface ServerStatusEntry { status: StatusValue; lastChecked: string; } interface ServerStatusContextType { statuses: Map; isLoading: boolean; refreshStatuses: () => Promise; getStatus: (hostId: number) => StatusValue; } const ServerStatusContext = createContext(null); const POLL_INTERVAL = 30000; export function ServerStatusProvider({ children, isAuthenticated = false, }: { children: React.ReactNode; isAuthenticated?: boolean; }) { const [statuses, setStatuses] = useState>( new Map(), ); const [isLoading, setIsLoading] = useState(false); const [enabledHostIds, setEnabledHostIds] = useState>(new Set()); const mountedRef = useRef(true); const enabledHostIdsRef = useRef(enabledHostIds); useEffect(() => { enabledHostIdsRef.current = enabledHostIds; }, [enabledHostIds]); const fetchEnabledHosts = useCallback(async () => { if (!isAuthenticated) { return new Set(); } try { const hosts = await getSSHHosts(); const enabled = new Set(); hosts.forEach((host) => { const statsConfig = (() => { try { return host.statsConfig ? JSON.parse(host.statsConfig) : DEFAULT_STATS_CONFIG; } catch { return DEFAULT_STATS_CONFIG; } })(); if (statsConfig.statusCheckEnabled !== false) { enabled.add(host.id); } }); setEnabledHostIds((prev) => { if (prev.size !== enabled.size) return enabled; for (const id of enabled) { if (!prev.has(id)) return enabled; } return prev; }); return enabled; } catch { return new Set(); } }, [isAuthenticated]); const refreshStatuses = useCallback(async () => { if (!mountedRef.current || !isAuthenticated) return; setIsLoading(true); try { const data = await getAllServerStatuses(); if (!mountedRef.current) return; const newStatuses = new Map(); const now = new Date().toISOString(); if (data && typeof data === "object") { Object.entries(data).forEach(([idStr, statusData]) => { const id = parseInt(idStr, 10); if (!isNaN(id)) { const status = statusData?.status === "online" ? "online" : "offline"; newStatuses.set(id, { status, lastChecked: statusData?.lastChecked || now, }); } }); } setStatuses(newStatuses); } catch { if (mountedRef.current) { setStatuses((prev) => { const updated = new Map(prev); enabledHostIdsRef.current.forEach((id) => { const existing = updated.get(id); updated.set(id, { status: "degraded", lastChecked: existing?.lastChecked || new Date().toISOString(), }); }); return updated; }); } } finally { if (mountedRef.current) { setIsLoading(false); } } }, [isAuthenticated]); const stableEnabledHostIds = useMemo( () => enabledHostIds, [[...enabledHostIds].sort().join(",")], ); const getStatus = useCallback( (hostId: number): StatusValue => { if (!stableEnabledHostIds.has(hostId)) { return "offline"; } return statuses.get(hostId)?.status || "degraded"; }, [statuses, stableEnabledHostIds], ); useEffect(() => { mountedRef.current = true; const init = async () => { await fetchEnabledHosts(); await refreshStatuses(); }; init(); const intervalId = setInterval(refreshStatuses, POLL_INTERVAL); return () => { mountedRef.current = false; clearInterval(intervalId); }; }, [fetchEnabledHosts, refreshStatuses]); useEffect(() => { const handleHostsChanged = async () => { await fetchEnabledHosts(); await refreshStatuses(); }; window.addEventListener("ssh-hosts:changed", handleHostsChanged); window.addEventListener("hosts:refresh", handleHostsChanged); return () => { window.removeEventListener("ssh-hosts:changed", handleHostsChanged); window.removeEventListener("hosts:refresh", handleHostsChanged); }; }, [fetchEnabledHosts, refreshStatuses]); return ( {children} ); } export function useServerStatus() { const context = useContext(ServerStatusContext); if (!context) { throw new Error( "useServerStatus must be used within a ServerStatusProvider", ); } return context; } export function useHostStatus( hostId: number, statusCheckEnabled: boolean = true, ) { const { getStatus } = useServerStatus(); if (!statusCheckEnabled) { return "offline" as StatusValue; } return getStatus(hostId); }