Add update progress polling overlay and strengthen state flags

This commit is contained in:
Aaron
2025-12-10 20:08:28 -05:00
parent 9f17c1a087
commit 712efba6f9
3 changed files with 49 additions and 8 deletions

View File

@@ -5,6 +5,7 @@ import re
import urllib.request
import hashlib
import fcntl
from functools import partial
HOST = "127.0.0.1"
PORT = 4000
@@ -598,6 +599,7 @@ def apply_update_stub():
manifest = None
state["in_progress"] = True
state["status"] = "in_progress"
state["progress"] = "Starting update…"
save_update_state(state)
@@ -642,7 +644,8 @@ def apply_update_stub():
ensure_dir(backup_dir)
# Backup web and api
if WEB_ROOT.exists():
shutil.copytree(WEB_ROOT, backup_dir / "pikit-web")
ensure_dir(backup_dir / "pikit-web")
shutil.copytree(WEB_ROOT, backup_dir / "pikit-web", dirs_exist_ok=True)
if API_PATH.exists():
shutil.copy2(API_PATH, backup_dir / "pikit-api.py")
@@ -672,7 +675,7 @@ def apply_update_stub():
state["progress"] = None
save_update_state(state)
except urllib.error.HTTPError as e:
state["status"] = "up_to_date"
state["status"] = "error"
state["message"] = f"No release available ({e.code})"
except Exception as e:
state["status"] = "error"
@@ -714,17 +717,24 @@ def rollback_update_stub():
state["message"] = "Another update is running"
save_update_state(state)
return state
state["in_progress"] = True
state["status"] = "in_progress"
state["progress"] = "Rolling back…"
save_update_state(state)
backups = sorted(BACKUP_ROOT.glob("*"), reverse=True)
if not backups:
state["status"] = "error"
state["message"] = "No backup available to rollback."
state["in_progress"] = False
state["progress"] = None
save_update_state(state)
release_lock(lock)
return state
target = backups[0]
try:
if (target / "pikit-web").exists():
shutil.rmtree(WEB_ROOT, ignore_errors=True)
shutil.copytree(target / "pikit-web", WEB_ROOT)
shutil.copytree(target / "pikit-web", WEB_ROOT, dirs_exist_ok=True)
if (target / "pikit-api.py").exists():
shutil.copy2(target / "pikit-api.py", API_PATH)
os.chmod(API_PATH, 0o755)
@@ -735,9 +745,10 @@ def rollback_update_stub():
except Exception as e:
state["status"] = "error"
state["message"] = f"Rollback failed: {e}"
state["in_progress"] = False
state["progress"] = None
save_update_state(state)
if lock:
release_lock(lock)
release_lock(lock)
return state

View File

@@ -106,6 +106,7 @@ const busyOverlay = document.getElementById("busyOverlay");
const busyTitle = document.getElementById("busyTitle");
const busyText = document.getElementById("busyText");
const toastContainer = document.getElementById("toastContainer");
const readyOverlay = document.getElementById("readyOverlay");
const TOAST_POS_KEY = "pikit-toast-pos";
const TOAST_ANIM_KEY = "pikit-toast-anim";
@@ -398,6 +399,7 @@ async function loadReleaseStatus() {
auto_check = false,
progress = null,
} = data || {};
window.__lastReleaseState = data;
setReleaseChip(data);
if (releaseCurrent) releaseCurrent.textContent = current_version;
if (releaseLatest) releaseLatest.textContent = latest_version;
@@ -485,9 +487,10 @@ function wireReleaseControls() {
releaseApplyBtn?.addEventListener("click", async () => {
try {
showBusy("Updating Pi-Kit…", "Applying release. This can take up to a minute.");
if (releaseProgress) releaseProgress.textContent = "Downloading and installing…";
await applyRelease();
await loadReleaseStatus();
pollReleaseStatus();
showToast("Update started", "success");
} catch (e) {
showToast(e.error || "Update failed", "error");
@@ -498,10 +501,11 @@ function wireReleaseControls() {
releaseRollbackBtn?.addEventListener("click", async () => {
try {
showBusy("Rolling back…", "Restoring previous backup.");
if (releaseProgress) releaseProgress.textContent = "Rolling back…";
await rollbackRelease();
await loadReleaseStatus();
showToast("Rollback complete", "success");
pollReleaseStatus();
showToast("Rollback started", "success");
} catch (e) {
showToast(e.error || "Rollback failed", "error");
} finally {
@@ -520,6 +524,27 @@ function wireReleaseControls() {
});
}
function pollReleaseStatus() {
let attempts = 0;
const maxAttempts = 30; // ~1 min at 2s
const tick = async () => {
attempts += 1;
await loadReleaseStatus();
const state = window.__lastReleaseState || {};
if (state.status === "in_progress" && attempts < maxAttempts) {
setTimeout(tick, 2000);
} else {
hideBusy();
if (state.status === "up_to_date") {
showToast("Update complete", "success");
} else if (state.status === "error") {
showToast(state.message || "Update failed", "error");
}
}
};
tick();
}
function showBusy(title = "Working…", text = "This may take a few seconds.") {
if (!busyOverlay) return;
busyTitle.textContent = title;

View File

@@ -1374,6 +1374,11 @@ select:focus-visible,
#addServiceModal .controls {
padding: 0 2px 4px;
}
/* Busy overlay already defined; ensure modal width for release modal */
#releaseModal .modal-card.wide {
max-width: 760px;
}
.modal:not(.hidden) .modal-card {
transform: translateY(0) scale(1);
}