aureon-os / frontend /src /hooks /useOperatorPulse.ts
GaryLeckey's picture
Deploy Aureon OS (deploy/selfhost stack: nginx edge + fusion API x2 + console + HNC organ + redis + postgres; paper mode) (part 18)
f568a4f verified
Raw History Blame Contribute Delete
2.32 kB
/**
* useOperatorPulse — polls the operator's one-call vitals (`GET /api/pulse`) and
* reports an HONEST connection state the whole UI can trust.
*
* Returns `offline: true` the moment the gateway is unreachable (transport
* failure / timeout), distinct from a reachable-but-degraded backend. Dashboards
* use this to show real numbers when live and a clear "backend offline" notice
* otherwise — never fabricated data.
*/
import { useEffect, useRef, useState } from "react";
import { ApiError, fetchPulse, type OperatorPulse } from "@/services/apiClient";
export interface PulseState {
pulse: OperatorPulse | null;
/** true when the operator backend is unreachable (gateway down / timeout). */
offline: boolean;
/** true until the first poll resolves. */
loading: boolean;
/** last error message, if any (offline or HTTP). */
error: string | null;
/** epoch ms of the last successful read, or null if never. */
lastOkAt: number | null;
}
const DEFAULT_INTERVAL_MS = 15_000;
export function useOperatorPulse(intervalMs: number = DEFAULT_INTERVAL_MS): PulseState {
const [state, setState] = useState<PulseState>({
pulse: null,
offline: false,
loading: true,
error: null,
lastOkAt: null,
});
const lastOk = useRef<number | null>(null);
useEffect(() => {
let cancelled = false;
const poll = async () => {
try {
const pulse = await fetchPulse();
if (cancelled) return;
lastOk.current = Date.now();
setState({ pulse, offline: false, loading: false, error: null, lastOkAt: lastOk.current });
} catch (err) {
if (cancelled) return;
const offline = err instanceof ApiError ? err.offline : true;
const message = err instanceof Error ? err.message : "unknown error";
setState((prev) => ({
// keep the last-known pulse visible but flag the state honestly
pulse: prev.pulse,
offline,
loading: false,
error: message,
lastOkAt: lastOk.current,
}));
}
};
poll();
const t = window.setInterval(poll, intervalMs);
return () => {
cancelled = true;
window.clearInterval(t);
};
}, [intervalMs]);
return state;
}