feat: SSH card UX, agent version sync, persistent ssh_admin_ok (0.11.5)

Silent SSH probe on host card open; manual test feedback auto-hides. Persist ssh_admin_ok in DB (migration 019). After agent update read version from host and refresh UI.
This commit is contained in:
2026-06-20 01:01:56 +10:00
parent 6fea8262fb
commit 2eb06acb5b
17 changed files with 423 additions and 70 deletions
+11
View File
@@ -42,3 +42,14 @@ export function isAgentVersionOutdated(
if (lag === null) return true;
return lag >= lagThreshold;
}
export function isAgentVersionNewer(
candidate: string | null | undefined,
current: string | null | undefined,
): boolean {
const next = parseAgentVersion(candidate);
const prev = parseAgentVersion(current);
if (!next) return false;
if (!prev) return true;
return compareVersions(next, prev) > 0;
}
+16
View File
@@ -0,0 +1,16 @@
import type { HostDetail } from "../api";
type HostPatchListener = (detail: HostDetail) => void;
const listeners = new Set<HostPatchListener>();
export function subscribeHostPatch(listener: HostPatchListener): () => void {
listeners.add(listener);
return () => listeners.delete(listener);
}
export function emitHostPatch(detail: HostDetail): void {
for (const listener of listeners) {
listener(detail);
}
}
+12
View File
@@ -1,4 +1,5 @@
import type { HostDetail, HostSummary } from "../api";
import { isAgentVersionNewer } from "./agentVersion";
/** Обновляет поля строки списка хостов из GET /hosts/{id} (без перезагрузки таблицы). */
export function patchHostSummaryFromDetail(target: HostSummary, detail: HostDetail): void {
@@ -13,3 +14,14 @@ export function patchHostSummaryFromDetail(target: HostSummary, detail: HostDeta
target.last_inventory_at = detail.last_inventory_at;
target.agent_status = detail.agent_status;
}
export function bumpLatestAgentVersion(
latest: Record<string, string> | undefined,
product: string,
version: string | null | undefined,
): void {
if (!latest || !version) return;
if (isAgentVersionNewer(version, latest[product])) {
latest[product] = version;
}
}