/** * `useServerStatus` hook. * * Fetches and periodically refreshes the fail2ban server health snapshot * from `GET /api/dashboard/status`. Also refetches on window focus so the * status is always fresh when the user returns to the tab. */ import { useCallback, useEffect, useRef, useState } from "react"; import { fetchServerStatus } from "../api/dashboard"; import type { ServerStatus } from "../types/server"; /** How often to poll the status endpoint (milliseconds). */ const POLL_INTERVAL_MS = 30_000; /** Return value of the {@link useServerStatus} hook. */ export interface UseServerStatusResult { /** The most recent server status snapshot, or `null` before the first fetch. */ status: ServerStatus | null; /** BanGUI application version string. */ banguiVersion: string | null; /** Whether a fetch is currently in flight. */ loading: boolean; /** Error message string when the last fetch failed, otherwise `null`. */ error: string | null; /** Manually trigger a refresh immediately. */ refresh: () => void; } /** * Poll `GET /api/dashboard/status` every 30 seconds and on window focus. * * @returns Current status, loading state, error, and a `refresh` callback. */ export function useServerStatus(): UseServerStatusResult { const [status, setStatus] = useState(null); const [banguiVersion, setBanguiVersion] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Use a ref so the fetch function identity is stable. const fetchRef = useRef<() => Promise>(async () => Promise.resolve()); const doFetch = useCallback(async (): Promise => { setLoading(true); try { const data = await fetchServerStatus(); setStatus(data.status); setBanguiVersion(data.bangui_version); setError(null); } catch (err: unknown) { setError(err instanceof Error ? err.message : "Failed to fetch server status"); } finally { setLoading(false); } }, []); fetchRef.current = doFetch; // Initial fetch + polling interval. useEffect((): (() => void) => { void doFetch().catch((): void => undefined); const id = setInterval((): void => { void fetchRef.current().catch((): void => undefined); }, POLL_INTERVAL_MS); return (): void => { clearInterval(id); }; }, [doFetch]); // Refetch on window focus. useEffect(() => { const onFocus = (): void => { void fetchRef.current(); }; window.addEventListener("focus", onFocus); return (): void => { window.removeEventListener("focus", onFocus); }; }, []); const refresh = useCallback((): void => { void doFetch().catch((): void => undefined); }, [doFetch]); return { status, banguiVersion, loading, error, refresh }; }