From b3c1fbc9bd1f5999da220886529de998602aca81 Mon Sep 17 00:00:00 2001 From: Kroonk Date: Thu, 21 May 2026 18:25:27 +0200 Subject: [PATCH] feat: fix watchtower manual run stuck states in both client and server --- admin.html | 2 +- admin.js | 20 +++++++++++++++++++- server.js | 17 +++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/admin.html b/admin.html index 25e158e..13ced18 100644 --- a/admin.html +++ b/admin.html @@ -90,6 +90,6 @@ - + diff --git a/admin.js b/admin.js index 1e6cb17..935980b 100644 --- a/admin.js +++ b/admin.js @@ -646,7 +646,7 @@ async function pollWatchtowerRun(runId, attempt = 0) { lastManualWatchtowerRun = payload; renderWatchtower(latestWatchtower); - if (payload.running && attempt < 36) { + if (payload.running && attempt < 60) { opsRefreshTimer = window.setTimeout(() => pollWatchtowerRun(runId, attempt + 1), 5000); return; } @@ -668,6 +668,24 @@ async function refreshOps(options = {}) { } } + if (lastManualWatchtowerRun && lastManualWatchtowerRun.running) { + try { + const runId = lastManualWatchtowerRun.id; + const response = await fetch(`/api/watchtower/runs/${encodeURIComponent(runId)}`, { + cache: 'no-store', + headers: { + Authorization: `Bearer ${tokenSet?.id_token}` + } + }); + if (response.ok) { + const payload = await response.json().catch(() => ({})); + lastManualWatchtowerRun = payload; + } + } catch (e) { + console.warn('Failed to refresh manual watchtower run status:', e); + } + } + try { const response = await fetch('/api/status', { cache: 'no-store' }); if (!response.ok) throw new Error(`HTTP ${response.status}`); diff --git a/server.js b/server.js index 05c60c4..b912e56 100644 --- a/server.js +++ b/server.js @@ -461,12 +461,25 @@ async function getWatchtowerRun(runId) { } if (manualWatchtowerRuns.has(id)) { - return manualWatchtowerRuns.get(id); + const cached = manualWatchtowerRuns.get(id); + if (!cached.running) { + return cached; + } } try { - return await readWatchtowerRunContainer(id); + const livePayload = await readWatchtowerRunContainer(id); + manualWatchtowerRuns.set(id, livePayload); + + if (!livePayload.running) { + removeContainer(id).catch(() => {}); + } + + return livePayload; } catch (error) { + if (manualWatchtowerRuns.has(id)) { + return manualWatchtowerRuns.get(id); + } error.statusCode = error.statusCode || 404; throw error; }