"use strict"; // ── Update modal ────────────────────────────────────────────────── function openUpdateModal() { if (!$modal) return; _updateLog = ""; _updateLogOffset = 0; _serverWasDown = false; _updateFinished = false; if ($modalLog) $modalLog.textContent = ""; if ($modalStatus) $modalStatus.textContent = "Starting update…"; if ($modalSpinner) $modalSpinner.classList.add("spinning"); if ($btnReboot) $btnReboot.style.display = "none"; if ($btnSave) $btnSave.style.display = "none"; if ($btnCloseModal) $btnCloseModal.disabled = true; $modal.classList.add("open"); startUpdate(); } function closeUpdateModal() { if (!$modal) return; $modal.classList.remove("open"); stopUpdatePoll(); } function appendLog(text) { if (!text) return; _updateLog += text; if ($modalLog) { $modalLog.textContent += text; $modalLog.scrollTop = $modalLog.scrollHeight; } } function startUpdate() { fetch("/api/updates/run", { method: "POST" }) .then(function(response) { if (!response.ok) return response.text().then(function(t) { throw new Error(t); }); return response.json(); }) .then(function(data) { if (data.status === "already_running") appendLog("[Update already in progress, attaching…]\n\n"); if ($modalStatus) $modalStatus.textContent = "Updating…"; startUpdatePoll(); }) .catch(function(err) { appendLog("[Error: failed to start update — " + err + "]\n"); onUpdateDone(false); }); } function startUpdatePoll() { pollUpdateStatus(); _updatePollTimer = setInterval(pollUpdateStatus, UPDATE_POLL_INTERVAL); } function stopUpdatePoll() { if (_updatePollTimer) { clearInterval(_updatePollTimer); _updatePollTimer = null; } } async function pollUpdateStatus() { if (_updateFinished) return; try { var data = await apiFetch("/api/updates/status?offset=" + _updateLogOffset); if (_serverWasDown) { _serverWasDown = false; appendLog("[Server reconnected]\n"); if ($modalStatus) $modalStatus.textContent = "Updating…"; } if (data.log) appendLog(data.log); _updateLogOffset = data.offset; if (data.running) return; _updateFinished = true; stopUpdatePoll(); if (data.result === "success") onUpdateDone(true); else onUpdateDone(false); } catch (err) { if (!_serverWasDown) { _serverWasDown = true; appendLog("\n[Server restarting — waiting for it to come back…]\n"); if ($modalStatus) $modalStatus.textContent = "Server restarting…"; } } } function onUpdateDone(success) { if ($modalSpinner) $modalSpinner.classList.remove("spinning"); if ($btnCloseModal) $btnCloseModal.disabled = false; if (success) { if ($modalStatus) $modalStatus.textContent = "✓ Update complete"; if ($btnReboot) $btnReboot.style.display = "inline-flex"; } else { if ($modalStatus) $modalStatus.textContent = "✗ Update failed"; if ($btnSave) $btnSave.style.display = "inline-flex"; if ($btnReboot) $btnReboot.style.display = "inline-flex"; } } function saveErrorReport() { var blob = new Blob([_updateLog], { type: "text/plain" }); var url = URL.createObjectURL(blob); var a = document.createElement("a"); a.href = url; a.download = "sovran-update-error-" + new Date().toISOString().split(".")[0].replace(/:/g, "-") + ".txt"; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // ── Reboot ──────────────────────────────────────────────────────── function doReboot() { if ($modal) $modal.classList.remove("open"); if ($rebuildModal) $rebuildModal.classList.remove("open"); stopUpdatePoll(); stopRebuildPoll(); if ($rebootOverlay) $rebootOverlay.classList.add("visible"); fetch("/api/reboot", { method: "POST" }).catch(function() {}); setTimeout(waitForServerReboot, REBOOT_CHECK_INTERVAL); } function waitForServerReboot() { fetch("/api/config", { cache: "no-store" }) .then(function(res) { if (res.ok) window.location.reload(); else setTimeout(waitForServerReboot, REBOOT_CHECK_INTERVAL); }) .catch(function() { setTimeout(waitForServerReboot, REBOOT_CHECK_INTERVAL); }); }