milestone 30: overview as a dashboard, explicit health contract, period aggregations
Gates / frontend (push) Successful in 1m32s
Gates / test (push) Successful in 1m54s
Gates / package (push) Successful in 5m28s
Gates / container (push) Successful in 14s
Gates / test-aarch64 (push) Failing after 3h10m0s
CI / gates (push) Failing after 3h11m55s
Gates / frontend (push) Successful in 1m32s
Gates / test (push) Successful in 1m54s
Gates / package (push) Successful in 5m28s
Gates / container (push) Successful in 14s
Gates / test-aarch64 (push) Failing after 3h10m0s
CI / gates (push) Failing after 3h11m55s
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* The five conditions `GET /api/health` reports, as facts.
|
||||
*
|
||||
* A pure projection of `Health` so the whole matrix — every state of every
|
||||
* condition, and every way out a degraded one offers — is testable without a
|
||||
* router or a fetch. `HealthStrip` only paints what this returns.
|
||||
*
|
||||
* A healthy fact is quiet: no badge, no panel, no green reassurance, so the one
|
||||
* condition that is not healthy is the thing the eye lands on.
|
||||
*/
|
||||
|
||||
import { formatBytes, formatClock } from "@/lib/format";
|
||||
import type { Health } from "@/lib/types";
|
||||
|
||||
export type FactTone = "ok" | "notice" | "warn" | "danger";
|
||||
|
||||
/**
|
||||
* Where a degraded fact sends the reader. A `filter` link stays on this page and
|
||||
* narrows it to the component that failed; a `route` link leaves for the surface
|
||||
* that can fix the condition.
|
||||
*/
|
||||
export type FactLink =
|
||||
| { kind: "route"; to: "/blocklists" | "/upstreams"; label: string }
|
||||
| { kind: "filter"; component: string; label: string };
|
||||
|
||||
export interface HealthFact {
|
||||
key: "protection" | "upstreams" | "query_history" | "diagnostics" | "disk";
|
||||
label: string;
|
||||
tone: FactTone;
|
||||
/** The state, in the reader's words. Never colour alone. */
|
||||
value: string;
|
||||
detail?: string;
|
||||
link?: FactLink;
|
||||
}
|
||||
|
||||
const numberFormat = new Intl.NumberFormat();
|
||||
|
||||
/** Kept out of the state rule: rows are lost whether or not the box is losing them now. */
|
||||
function dropText(dropped: number, lastDrop: number | null, locale?: string, timeZone?: string): string | undefined {
|
||||
if (dropped <= 0) return undefined;
|
||||
const count = `${numberFormat.format(dropped)} ${dropped === 1 ? "query" : "queries"} dropped`;
|
||||
return lastDrop === null ? count : `${count}, last at ${formatClock(lastDrop, locale, timeZone)}`;
|
||||
}
|
||||
|
||||
function protectionFact(protection: Health["protection"], locale?: string, timeZone?: string): HealthFact {
|
||||
if (protection.state === "unavailable") {
|
||||
return {
|
||||
key: "protection",
|
||||
label: "Protection",
|
||||
tone: "danger",
|
||||
value: "Unavailable",
|
||||
detail: "No filter snapshot is published, so queries are not being filtered.",
|
||||
link: { kind: "route", to: "/blocklists", label: "Blocklists" },
|
||||
};
|
||||
}
|
||||
if (protection.state === "paused") {
|
||||
return {
|
||||
key: "protection",
|
||||
label: "Protection",
|
||||
tone: "notice",
|
||||
value:
|
||||
protection.until === null
|
||||
? "Paused"
|
||||
: `Paused until ${formatClock(protection.until, locale, timeZone)}`,
|
||||
};
|
||||
}
|
||||
return { key: "protection", label: "Protection", tone: "ok", value: "Active" };
|
||||
}
|
||||
|
||||
function queryHistoryFact(history: Health["query_history"], locale?: string, timeZone?: string): HealthFact {
|
||||
const detail = dropText(history.dropped_total, history.last_drop_s, locale, timeZone);
|
||||
if (history.state === "failed") {
|
||||
return {
|
||||
key: "query_history",
|
||||
label: "Query history",
|
||||
tone: "danger",
|
||||
value: "Writer failed",
|
||||
detail,
|
||||
link: { kind: "filter", component: "query_log", label: "Query log diagnostics" },
|
||||
};
|
||||
}
|
||||
if (history.state === "losing") {
|
||||
// The disk gate is what is holding the writes back, and it may be the only
|
||||
// thing that has reported: query_log itself need not have an episode open.
|
||||
return {
|
||||
key: "query_history",
|
||||
label: "Query history",
|
||||
tone: "danger",
|
||||
value: "Losing rows",
|
||||
detail,
|
||||
link: { kind: "filter", component: "disk", label: "Disk diagnostics" },
|
||||
};
|
||||
}
|
||||
return { key: "query_history", label: "Query history", tone: "ok", value: "Recording", detail };
|
||||
}
|
||||
|
||||
export function healthFacts(health: Health, locale?: string, timeZone?: string): HealthFact[] {
|
||||
const { upstreams, diagnostics, disk } = health;
|
||||
return [
|
||||
protectionFact(health.protection, locale, timeZone),
|
||||
{
|
||||
key: "upstreams",
|
||||
label: "Upstreams",
|
||||
tone: upstreams.state === "unavailable" ? "danger" : "ok",
|
||||
value: upstreams.state === "unavailable" ? "None reachable" : "Available",
|
||||
detail: `${upstreams.available} of ${upstreams.total} enabled`,
|
||||
...(upstreams.state === "unavailable"
|
||||
? { link: { kind: "route", to: "/upstreams", label: "Upstreams" } as FactLink }
|
||||
: {}),
|
||||
},
|
||||
queryHistoryFact(health.query_history, locale, timeZone),
|
||||
diagnostics.state === "unavailable"
|
||||
? {
|
||||
key: "diagnostics",
|
||||
label: "Diagnostics",
|
||||
tone: "danger",
|
||||
value: "Unavailable",
|
||||
// No link: the page it would filter is the thing that is broken.
|
||||
detail: "Diagnostics are not being recorded. Check free disk space and the configuration database's permissions.",
|
||||
}
|
||||
: { key: "diagnostics", label: "Diagnostics", tone: "ok", value: "Recording" },
|
||||
{
|
||||
key: "disk",
|
||||
label: "Storage",
|
||||
tone: disk.state === "critical" ? "danger" : disk.state === "low" ? "warn" : "ok",
|
||||
value: disk.state === "critical" ? "Critical" : disk.state === "low" ? "Low" : "OK",
|
||||
detail: `${formatBytes(disk.free_bytes)} free`,
|
||||
...(disk.state === "ok"
|
||||
? {}
|
||||
: { link: { kind: "filter", component: "disk", label: "Disk diagnostics" } as FactLink }),
|
||||
},
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user