push terminal session changes and alert evaluator health

This commit is contained in:
lda committed 2026-10-01 11:32:26 +07:00 Verified
1 parent cf7622e818
commit f7c982cbd7
11 files changed
+663 -101

No files matched your search

+58 -11
View File
@@ -1,4 +1,4 @@
import { lazy, Suspense, useEffect, useState } from "react";
import { lazy, Suspense, useEffect, useRef, useState } from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import { toast } from "sonner";
@@ -40,22 +40,48 @@ export function App() {
const [history, setHistory] = useState<AlertTransition[]>([]);
const [audit, setAudit] = useState<AuditEvent[]>([]);
const [selectedAgentId, setSelectedAgentId] = useState("");
const [alertsHealth, setAlertsHealth] = useState<{
available: boolean;
reason?: string;
} | null>(null);
const [alertsStreamConnected, setAlertsStreamConnected] = useState(false);
const streamHealthReceived = useRef(false);
const alertsRevision = useRef(0);
const [state, setState] = useState<LoadState>("idle");
async function loadAlertsWithoutBlocking() {
try {
const nextAlerts = await fetchAlerts();
if (!streamHealthReceived.current) {
setAlertsHealth({ available: true });
}
return nextAlerts;
} catch (err) {
if (!streamHealthReceived.current) {
setAlertsHealth({ available: false, reason: String(err) });
}
return null;
}
}
async function loadAll() {
const revision = alertsRevision.current;
setState("loading");
try {
const [nextAgents, nextAlerts, nextHistory, nextAudit] =
await Promise.all([
fetchAgents(),
fetchAlerts(),
loadAlertsWithoutBlocking(),
fetchAlertHistory(20),
fetchAudit(30),
]);
setAgents(nextAgents);
setAlerts(nextAlerts);
setHistory(nextHistory);
// Streamed state wins over HTTP requests started before its arrival.
if (revision === alertsRevision.current) {
if (nextAlerts) setAlerts(nextAlerts);
setHistory(nextHistory);
}
setAudit(nextAudit);
const firstConnectedAgentId =
nextAgents.find((agent) => agent.connected)?.agent_id ?? "";
@@ -100,12 +126,15 @@ export function App() {
}
async function refreshAlertsAndHistory() {
const revision = alertsRevision.current;
const [nextAlerts, nextHistory] = await Promise.all([
fetchAlerts(),
loadAlertsWithoutBlocking(),
fetchAlertHistory(20),
]);
setAlerts(nextAlerts);
setHistory(nextHistory);
if (revision === alertsRevision.current) {
if (nextAlerts) setAlerts(nextAlerts);
setHistory(nextHistory);
}
}
useEffect(() => {
@@ -117,15 +146,14 @@ export function App() {
let disposed = false;
let attempt = 0;
let openedAt = 0;
let wsGeneration = 0;
let reconnectTimer: number | undefined;
const resync = () => {
const generation = wsGeneration;
const generation = alertsRevision.current;
void Promise.all([fetchAlerts(), fetchAlertHistory(20)])
.then(([nextAlerts, nextHistory]) => {
// A WS snapshot delivered while this request was in flight is newer.
if (generation !== wsGeneration) return;
if (disposed || generation !== alertsRevision.current) return;
setAlerts(nextAlerts);
setHistory(nextHistory);
})
@@ -138,18 +166,32 @@ export function App() {
socket = new WebSocket(wsUrl);
socket.onopen = () => {
if (disposed) return;
openedAt = Date.now();
setAlertsStreamConnected(true);
// Pull anything missed while the stream was down.
resync();
};
socket.onmessage = (evt) => {
wsGeneration += 1;
if (disposed) return;
alertsRevision.current += 1;
try {
const payload = JSON.parse(String(evt.data)) as {
type?: string;
available?: boolean;
reason?: string;
alerts?: Alert[];
recent_transitions?: AlertTransition[];
};
if (payload.type === "alerts_health") {
streamHealthReceived.current = true;
setAlertsHealth({
available: payload.available === true,
reason: payload.reason,
});
return;
}
if (payload.alerts) setAlerts(payload.alerts);
if (payload.recent_transitions)
setHistory(payload.recent_transitions);
@@ -161,6 +203,7 @@ export function App() {
socket.onclose = () => {
socket = null;
if (disposed) return;
setAlertsStreamConnected(false);
// Only a connection that stayed up proves the stream is healthy; an
// accept-then-close server must not pin us to the first backoff step.
if (openedAt && Date.now() - openedAt > 10_000) attempt = 0;
@@ -217,6 +260,8 @@ export function App() {
transitions={history}
audit={audit}
loading={state === "loading"}
alertsHealth={alertsHealth}
alertsStreamConnected={alertsStreamConnected}
onRefresh={loadAll}
/>
}
@@ -259,6 +304,8 @@ export function App() {
<AlertsPage
alerts={alerts}
transitions={history}
alertsHealth={alertsHealth}
alertsStreamConnected={alertsStreamConnected}
onRefresh={refreshAlertsAndHistory}
/>
}