86 lines
2.8 KiB
TypeScript
86 lines
2.8 KiB
TypeScript
/**
|
|
* `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<ServerStatus | null>(null);
|
|
const [banguiVersion, setBanguiVersion] = useState<string | null>(null);
|
|
const [loading, setLoading] = useState<boolean>(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// Use a ref so the fetch function identity is stable.
|
|
const fetchRef = useRef<() => Promise<void>>(async () => Promise.resolve());
|
|
|
|
const doFetch = useCallback(async (): Promise<void> => {
|
|
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 };
|
|
}
|