swarm-ui: show only firing alerts on the alerts page and link to Grafana's list
The page listed every non-normal instance. It now keeps the ones in the Alerting state and links to Grafana's alert-groups view for the pending, no-data and error ones.
This commit is contained in:
parent
b5d2e310ba
commit
842b37dc33
2 changed files with 33 additions and 24 deletions
|
|
@ -14,7 +14,7 @@ domain, covers host-level detail for one hive.
|
|||
| `/agents` | every agent: status, config PR, wanted state, inference settings, resource caps; create agents (with their permissions, optionally from an [archetype](archetypes.md)), link forge, matrix and GitHub accounts |
|
||||
| `/agents/<name>/terminal` | one agent's live terminal |
|
||||
| `/agents/<name>/subagents/<subagent>/terminal` | one of that agent's subagents' live terminal |
|
||||
| `/alerts` | the alerts the swarm's Grafana holds now (firing, pending, no data, error), read through the controller with a Viewer service-account token |
|
||||
| `/alerts` | the alerts of the swarm's Grafana that are firing now, with a link to Grafana's full alert list for the pending, no-data and error ones; read through the controller with a Viewer service-account token |
|
||||
| `/jobs` | the controller's job graph — where agent creation and credential mints show progress |
|
||||
| `/schedules` | the scheduled prompts: add, edit, pause, resume and remove |
|
||||
| `/broadcast` | send one message to several agents at once, picked from a grid of their icons |
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
// <AlertsPage> — `/alerts`' content: the alerts the swarm's Grafana holds
|
||||
// now, from swarm-controller's `GET /api/alerts`. Same shape as `HivesPage`:
|
||||
// one fetch on a `RefreshIntervalPicker` cadence, rendered as a `Table`.
|
||||
// <AlertsPage> — `/alerts`' content: the alerts of the swarm's Grafana that
|
||||
// are firing now, from swarm-controller's `GET /api/alerts` (which also lists
|
||||
// pending, no-data and errored instances; those stay in Grafana). Same shape
|
||||
// as `HivesPage`: one fetch on a `RefreshIntervalPicker` cadence, rendered as
|
||||
// a `Table`.
|
||||
import { useState } from "preact/hooks";
|
||||
import { ApiErrorPanel } from "@hive/shared/api-error-panel.js";
|
||||
import { readApiError, type ProblemDetails } from "@hive/shared/api-error.js";
|
||||
import { Badge, type BadgeTone } from "@hive/shared/badge.js";
|
||||
import { Badge } from "@hive/shared/badge.js";
|
||||
import { Panel } from "../ui/panel/Panel.js";
|
||||
import { RelativeTime } from "../ui/relative-time/RelativeTime.js";
|
||||
import {
|
||||
|
|
@ -17,19 +19,18 @@ import { Table, type TableColumn } from "../ui/table/Table.js";
|
|||
interface Alert {
|
||||
name: string;
|
||||
// Grafana's state for the instance: `Alerting`, `Pending`, `NoData` or
|
||||
// `Error`. Anything else renders neutral.
|
||||
// `Error`.
|
||||
state: string;
|
||||
active_at: string | null;
|
||||
summary: string | null;
|
||||
labels: Record<string, string>;
|
||||
}
|
||||
|
||||
const STATE_TONE: Record<string, BadgeTone> = {
|
||||
Alerting: "negative",
|
||||
Error: "negative",
|
||||
NoData: "warning",
|
||||
Pending: "warning",
|
||||
};
|
||||
// Grafana's alert-groups view, where the instances this page leaves out are.
|
||||
const GRAFANA_ALERTS_URL =
|
||||
"https://grafana.constellation.darkest.space/alerting/groups";
|
||||
|
||||
const FIRING = "Alerting";
|
||||
|
||||
// Grafana reports an instance that never became active with the zero time,
|
||||
// which `Date.parse` reads as year 1.
|
||||
|
|
@ -59,7 +60,7 @@ const COLUMNS: TableColumn<Alert>[] = [
|
|||
const since = activeSinceMs(a);
|
||||
return (
|
||||
<Badge
|
||||
tone={STATE_TONE[a.state] ?? "neutral"}
|
||||
tone="negative"
|
||||
value={
|
||||
since !== null ? (
|
||||
<>
|
||||
|
|
@ -72,9 +73,7 @@ const COLUMNS: TableColumn<Alert>[] = [
|
|||
/>
|
||||
);
|
||||
},
|
||||
sortBy: (a) => a.state,
|
||||
filterValues: (a) => [a.state],
|
||||
filterMode: "multiselect",
|
||||
sortBy: (a) => activeSinceMs(a) ?? 0,
|
||||
},
|
||||
{
|
||||
key: "summary",
|
||||
|
|
@ -90,6 +89,7 @@ const DEFAULT_INTERVAL_MS: RefreshIntervalMs = 30_000;
|
|||
export function AlertsPage() {
|
||||
const [alerts, setAlerts] = useState<Alert[] | null>(null);
|
||||
const [error, setError] = useState<ProblemDetails | null>(null);
|
||||
const firing = alerts?.filter((a) => a.state === FIRING) ?? null;
|
||||
const [intervalMs, setIntervalMs] =
|
||||
useState<RefreshIntervalMs>(DEFAULT_INTERVAL_MS);
|
||||
|
||||
|
|
@ -110,11 +110,20 @@ export function AlertsPage() {
|
|||
title="alerts"
|
||||
icon="🚨"
|
||||
actions={
|
||||
<RefreshIntervalPicker
|
||||
id="alerts-refresh"
|
||||
value={intervalMs}
|
||||
onChange={setIntervalMs}
|
||||
/>
|
||||
<>
|
||||
<a
|
||||
href={GRAFANA_ALERTS_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
all alerts in Grafana
|
||||
</a>
|
||||
<RefreshIntervalPicker
|
||||
id="alerts-refresh"
|
||||
value={intervalMs}
|
||||
onChange={setIntervalMs}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
{error ? (
|
||||
|
|
@ -124,11 +133,11 @@ export function AlertsPage() {
|
|||
/>
|
||||
) : null}
|
||||
{!error && alerts === null ? <p>loading…</p> : null}
|
||||
{alerts && alerts.length === 0 ? <p>no alerts.</p> : null}
|
||||
{alerts && alerts.length > 0 ? (
|
||||
{firing && firing.length === 0 ? <p>nothing is alerting.</p> : null}
|
||||
{firing && firing.length > 0 ? (
|
||||
<Table
|
||||
columns={COLUMNS}
|
||||
rows={alerts}
|
||||
rows={firing}
|
||||
rowKey={alertKey}
|
||||
storageKey="swarm-ui:alerts:table-filters"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue