Web: replace unreliable post-OTA device poll with a countdown timer

After a successful OTA upload the web panel polled the device by
fetching "/" and waiting for a 200 OK response.  This approach
is fundamentally broken when the new firmware contains a freshly
generated self-signed certificate (e.g. from a clean CI build):

- The browser rejects the TLS handshake with a certificate error
  (MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT in Firefox; other browsers
  report different error names but behave identically).  The fetch()
  call throws a generic TypeError — the same type thrown for any other
  network failure such as a refused or timed-out connection.  Browsers
  intentionally provide no way to distinguish a cert rejection from a
  plain connectivity failure, so the catch block cannot tell whether
  the device is up-but-cert-changed or simply not yet online.

- Switching to an explicit http:// URL to sidestep TLS is not an
  option: the web panel is served over HTTPS, and browsers block
  active mixed content (fetch/XHR to HTTP) unconditionally from an
  HTTPS origin.

Because no network-based probe can reliably detect device readiness
under these constraints without changing how the certificate is
generated, replace the polling loop with a simple 10-second countdown
timer that unconditionally redirects to the login page.  The device
is typically back online within ~7 seconds, so the 10-second wait
provides a reasonable safety margin.
Dieser Commit ist enthalten in:
Valentin V. Bartenev
2026-05-16 01:51:06 +03:00
Ursprung 1018404b82
Commit 59f6df50c5
+8 -16
Datei anzeigen
@@ -2641,22 +2641,14 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
}
if (xhr.status === 200) {
barEl.style.width = "100%";
msgEl.textContent = "Done. Device is rebooting";
let wait = 4;
const iv = setInterval(async () => {
msgEl.textContent += '.';
if (msgEl.textContent.length > 85) { clearInterval(iv); msgEl.textContent = "Device did not come back online. =("; return; }
if (--wait) { return; }
wait = 1000; // prevent concurrent fetch
try {
if ((await fetch("/", { cache: "no-store" })).ok) {
clearInterval(iv);
msgEl.textContent = "Device is back online. Redirecting to login...";
setTimeout(redirectToLogin, 1500);
return;
}
} catch (_) {}
wait = 4;
let secs = 10;
const iv = setInterval(() => {
if (--secs <= 0) {
clearInterval(iv);
redirectToLogin();
return;
}
msgEl.textContent = `Done. Device is rebooting. Redirecting in ${secs}s...`;
}, 1000);
} else {
msgEl.textContent = "Failed: " + (xhr.responseText || String(xhr.status));