From 6fa54041f6a519f3c680e86addb4131af661e9ef Mon Sep 17 00:00:00 2001 From: Ivan Vasilev Date: Fri, 30 Oct 2026 00:16:11 +0300 Subject: [PATCH] Prometheus install: wait until the panel sees it before stopping the poll The status handler probed Prometheus before reading the job, so a poll could get exit 0 with ready=false; the page then stopped polling and the Monitor tab waited for App's 60 s check. --- awg-ui/src/tabs/settings/prometheus/index.tsx | 20 +++++++++++++------ internal/ui/monitor.go | 7 ++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/awg-ui/src/tabs/settings/prometheus/index.tsx b/awg-ui/src/tabs/settings/prometheus/index.tsx index 06c914e..88fc544 100644 --- a/awg-ui/src/tabs/settings/prometheus/index.tsx +++ b/awg-ui/src/tabs/settings/prometheus/index.tsx @@ -20,6 +20,7 @@ export interface MonitorStatus { // Прогресс операции из строк «N/M шаг», которые печатает сам установщик (cli/src/monitor.ts). Шаг N считаем // выполненным наполовину: так полоска не стоит на нуле, пока идёт долгий apt, и не обещает больше, чем известно. const STEP = /^(\d+)\/(\d+)\s+(.+)$/; +const SETTLE_MS = 90_000; function JobProgress({ job, starting }: { job: MonitorStatus['job']; starting: boolean }) { let step: [number, number, string] | null = null; for (const l of job?.log.split('\n') ?? []) { const m = STEP.exec(l.trim()); if (m) step = [Number(m[1]), Number(m[2]), m[3]]; } @@ -55,15 +56,22 @@ export default function PrometheusInstall({ token, showMsg }: PageProps) { useEffect(() => { void loadStatus(); }, [loadStatus]); - // Пока идёт установка или удаление — следим за ходом: раз в 2 с читаем статус и лог + // Пока идёт установка или удаление — следим за ходом: раз в 2 с читаем статус и лог. Операция + // закончилась, а панель ещё не видит результата (на ноде агент перезапускается и переподключается) — + // следим дальше, пока ready не совпадёт с ней, но не дольше SETTLE_MS: иначе «Готово» висит рядом + // с «не подключён», а пункт «Монитор» ждёт минутного опроса App. + const [settle, setSettle] = useState(null); // до какого момента ждём ready useEffect(() => { - if (!pending && !status?.job?.running) return; + if (!pending && !status?.job?.running && settle === null) return; const t = setInterval(async () => { const s = await loadStatus(); - if (s && !s.job?.running) setPending(null); + if (!s?.job || s.job.running) return; // агент ноды переподключается — job пока не виден + setPending(null); + const waiting = s.job.exit === 0 && s.ready !== (s.job.action === 'install'); + setSettle(d => waiting && (d === null || Date.now() < d) ? d ?? Date.now() + SETTLE_MS : null); }, 2000); return () => clearInterval(t); - }, [pending, status?.job?.running, loadStatus]); + }, [pending, status?.job?.running, settle, loadStatus]); const run = async (action: 'install' | 'remove') => { if (action === 'remove' && !confirm('Удалить Prometheus? Он будет остановлен, а накопленные графики (до 31 дня) сотрутся. Панель продолжит отдавать /metrics.')) return; @@ -75,7 +83,7 @@ export default function PrometheusInstall({ token, showMsg }: PageProps) { if (!status) return

Загрузка…

; const job = status.job; - const working = !!pending || !!job?.running; + const working = !!pending || !!job?.running || settle !== null; const doing = pending ?? (job?.running ? job.action : null); const showJob = !!pending || (!!job && (job.running || job.exit !== null)); const failed = !!job && job.exit !== null && job.exit !== 0; @@ -96,7 +104,7 @@ export default function PrometheusInstall({ token, showMsg }: PageProps) { - {doing === 'install' ? 'ставится' : doing === 'remove' ? 'удаляется' : status.ready ? 'работает' : 'не подключён'} + {doing === 'install' ? 'ставится' : doing === 'remove' ? 'удаляется' : settle !== null ? (status.ready ? 'отключается' : 'подключается') : status.ready ? 'работает' : 'не подключён'} {status.can_install && (status.ready ? (