fix: node-provisioning status poll could run forever, silently or stuck on "waiting"

Follow-on from the last commit's error-handling sweep — one more spot
that calls api() without a try/catch, but a different shape of problem
than the others: this one's a setInterval, not a one-shot action, so a
thrown/rejected promise inside it doesn't stop anything — the interval
just keeps firing every 4s regardless, forever, with each failure only
visible as an unhandled rejection in devtools. And even on the success
path there was no upper bound at all: if the node never actually comes
online (the admin closes the terminal before finishing the install
command, say), "Ожидаю установки…" just sits there indefinitely with
no way to know if it's still trying or has effectively given up.

Now: a consecutive-error counter that gives up after 5 straight
failures with a visible message pointing at the manual "Проверить"
button, and an overall 150-attempt cap (10 minutes at the existing 4s
interval) that stops polling and says so if the node genuinely never
reports active. A single transient failure doesn't trip either — the
error counter resets on any successful check, so one blip in an
otherwise-working poll doesn't cut it short.

Verification: extracted the poll callback's logic (can't spin up a real
setInterval usefully in a one-shot Node script) and drove it by calling
it directly in sequence, which is what setInterval does under the hood
anyway. 6 cases: quick success, a transient error that self-heals by
the next tick, 5 consecutive failures giving up with the right message
at exactly attempt 5, the 150-attempt timeout firing when status never
goes active, and confirming no further attempts happen at all once
either give-up path triggers — not just that the message stops
updating.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Savsis? 2026-09-14 09:09:36 +05:00
parent 2b34b11391
commit 695771c79a

View file

@ -1802,12 +1802,28 @@ async function generateGuide() {
<p class="page-sub" style="margin-top:12px" id="guide-status">Ожидаю установки…</p> <p class="page-sub" style="margin-top:12px" id="guide-status">Ожидаю установки…</p>
`; `;
if (pollTimer) clearInterval(pollTimer); if (pollTimer) clearInterval(pollTimer);
let pollAttempts = 0, pollErrors = 0;
pollTimer = setInterval(async () => { pollTimer = setInterval(async () => {
const st = await api(`/admin/api/nodes/${res.code}/status`); pollAttempts++;
let st;
try {
st = await api(`/admin/api/nodes/${res.code}/status`);
pollErrors = 0;
} catch (e) {
pollErrors++;
if (pollErrors >= 5) {
clearInterval(pollTimer);
document.getElementById("guide-status").innerHTML = '<span class="badge bad">Не получилось проверить статус (' + esc(e.message) + ') — проверь вручную кнопкой «Проверить» у ноды</span>';
}
return;
}
if (st.status === "active") { if (st.status === "active") {
clearInterval(pollTimer); clearInterval(pollTimer);
document.getElementById("guide-status").innerHTML = '<span class="badge ok">Установлено и подключено</span>'; document.getElementById("guide-status").innerHTML = '<span class="badge ok">Установлено и подключено</span>';
loadNodes(); loadNodes();
} else if (pollAttempts >= 150) {
clearInterval(pollTimer);
document.getElementById("guide-status").innerHTML = '<span class="badge warn">Не дождались за 10 минут — нода появится в списке сама, когда установка на сервере закончится</span>';
} }
}, 4000); }, 4000);
} }