import { useQuery } from "@tanstack/react-query"; import { configStatusQuery } from "@/lib/queries"; import type { ConfigStatus } from "@/lib/types"; /** * Configuration authority as the UI must treat it: three states, never two. * * `undefined` is not database mode. Until `/api/config/status` answers, the * running process may be file-managed, and a form rendered on that guess * invites edits the server will reject. So a page shows neither an editable * form nor a definition list until the query resolves, and a failed query is * an explicit error with a way to retry. */ export type Authority = | { state: "pending" } | { state: "failed"; error: unknown; retry: () => void } | { state: "resolved"; status: ConfigStatus }; /** * The running server's configuration status. Every page may call this — it is * the shared `["configStatus"]` key, so one subscription serves them all from * cache. */ export function useAuthority(): Authority { const query = useQuery(configStatusQuery()); if (query.isPending) return { state: "pending" }; if (query.isError) { return { state: "failed", error: query.error, retry: () => void query.refetch(), }; } return { state: "resolved", status: query.data }; } /** * True unless the server has said the database owns the configuration. * * The lock is global and it fails closed: pending, failed and `managed_file` * all read as locked, because only a resolved database authority proves a * configuration mutation can succeed. Runtime actions — pause, update now, * reload certificates, deleting an observed client, login and logout — do not * consult it; they work under every authority. */ export function useReadOnlyConfig(): boolean { const authority = useAuthority(); return !(authority.state === "resolved" && authority.status.authority === "database"); }