v0.147.0 — feedback slice 1: pressing a button says something
The systemic complaint, twice in one evening: you press a button and nothing happens. No progress, no ETA, no named result. Three worst offenders, fixed on the two patterns already here (deploy 3-step panel, storage-init status poll). No new framework — that is a ROADMAP item; three targeted cards ship tonight. 4a — a verification restore names its result. The flash said the app had been restored "to a verification folder on the drive"; which folder, on which drive, was invisible, so the customer could not go and look at what they had just asked for. Full path now. The restore page gained a listing of existing verification copies (app, size, date, path) — nothing anywhere showed these, so they piled up and the only way to find them was SSH — each with a double-confirmed delete. That delete is the only one this release adds, so it names a STACK, never a path: the Manager resolves the name inside a backups/offsite-restore root it computed itself and refuses anything landing outside. Red-proofed — neutralise the name guard and stack:"" resolves to the offsite-restore ROOT and takes every copy with it. Refusals are asserted as non-effects. 4b — Megosztás enable shows what it is waiting for. Enabling ran ReconcileSamba synchronously inside the POST handler; on a golden without felhom-samba baked that is compose pulling ~100MB, i.e. minutes of an apparently-hung form post followed by "Beállítás mentve." whether or not anything came up. Detached + polled now, distinguishing "képfájl letöltése" from "indítás" — decided BEFORE the work starts, since afterwards the image is always present. Success is probed, not inferred (compose up -d exits 0 on a crash-loop). The password form starts the same job: with UserSet false reconcile deploys nothing, so on a fresh box that is where the pull actually happens. 4c — "Távoli mentés most" streams real progress. restic was already reporting bytes and percent; the runner seam used CombinedOutput() and discarded them. The manual run now passes --json and scans stdout line-by-line: total bytes, percent, current app. Manual only — the nightly stays silent, pinned by a test that fails if it ever passes --json. The poll now arms unconditionally, closing a race the manual trigger always ran: the redirect rendered before the goroutine wrote LastStatus=running, so the poll never armed and the page sat static during the very run just started. Red-proofed twice. Also closes the golden/controller infra-image drift at the source: infra.Images() derives from the existing pins and --print-infra-images exposes it, so the golden bake can stop carrying its own copy. That copy had already drifted — felhom-samba was never added, so the golden baked 3 of 4, which is why enabling Megosztás pulled at runtime in the first place. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nn3VgQk9iwEGgyx6QJ2NvE
This commit is contained in:
@@ -126,6 +126,15 @@
|
||||
<button type="submit" class="btn btn-sm btn-primary">Távoli mentés most</button>
|
||||
</form>
|
||||
</div>
|
||||
<!-- v0.147.0 (4c): live progress for a MANUAL run — total bytes, percent and which app is being
|
||||
pushed, parsed from restic's --json status stream. Hidden until a run reports progress; the
|
||||
nightly run never populates it. -->
|
||||
<div id="offbox-progress" class="alert alert-info" style="display:none;margin-top:.75rem">
|
||||
<span id="offbox-progress-text"></span>
|
||||
<div class="progress-bar-task" style="margin-top:.5rem">
|
||||
<div id="offbox-progress-bar" class="progress-fill" style="width:0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<h4 style="margin-top:1.25rem">Mely alkalmazások mentődnek a távoli tárolóra?</h4>
|
||||
{{if and .OffboxApps (eq .Offbox.EscrowState "escrowed") (eq .OffboxToggledCount 0)}}
|
||||
<p class="form-hint">Nincs távoli mentésre jelölt alkalmazás — jelölj ki legalább egyet.</p>
|
||||
@@ -172,19 +181,55 @@
|
||||
{{end}}
|
||||
|
||||
<script>
|
||||
/* Part C (v0.142.0): while a remote backup is in flight the status card shows "Fut…" — poll the run
|
||||
status and reload once it reaches a terminal state, so the customer sees Rendben/Hiba + fresh
|
||||
numbers WITHOUT a manual reload. Polls only when a run is running; stops (page reload) at terminal. */
|
||||
/* Part C (v0.142.0) + 4c (v0.147.0): poll the run status, render live progress for a manual run, and
|
||||
reload once it reaches a terminal state so the customer sees Rendben/Hiba + fresh numbers WITHOUT a
|
||||
manual reload.
|
||||
|
||||
v0.147.0 changed WHEN polling starts. It used to begin only if the page already rendered "Fut…",
|
||||
which loses a race the manual trigger always runs: the POST redirects and this page renders before
|
||||
the detached goroutine has written LastStatus=running, so the poll never armed and the customer
|
||||
watched a static page during the very run they had just started. Now it always arms and gives up
|
||||
after GRACE quiet ticks if nothing is (or ever was) in flight. */
|
||||
(function(){
|
||||
var v=document.getElementById('offbox-status-value');
|
||||
if(!v || v.textContent.indexOf('Fut')<0) return; // not running → nothing to poll
|
||||
var timer=setInterval(function(){
|
||||
var v = document.getElementById('offbox-status-value');
|
||||
var box = document.getElementById('offbox-progress');
|
||||
var text = document.getElementById('offbox-progress-text');
|
||||
var bar = document.getElementById('offbox-progress-bar');
|
||||
var GRACE = 5; // ~15s of quiet before concluding nothing is running
|
||||
var quiet = 0;
|
||||
var sawRunning = (v && v.textContent.indexOf('Fut') >= 0);
|
||||
|
||||
var timer = setInterval(function(){
|
||||
fetch('/backup/offbox/status',{headers:{'Accept':'application/json'}})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(d){
|
||||
if(d && d.status==='running') return; // still running → keep polling
|
||||
clearInterval(timer);
|
||||
location.reload(); // terminal → re-render (fresh numbers, warnings, or the orphan card)
|
||||
if(!d) return;
|
||||
var p = d.progress || {};
|
||||
var running = (d.status === 'running') || p.active;
|
||||
|
||||
if(running){
|
||||
sawRunning = true; quiet = 0;
|
||||
if(p.active && box){
|
||||
/* Only claim a percentage once restic has told us a total — before the scan finishes,
|
||||
percent is 0 of 0, and a bar pinned at 0% reads as "stuck" rather than "measuring". */
|
||||
var label = p.current_app ? ('Mentés: ' + p.current_app) : 'Mentés folyamatban';
|
||||
if(p.total_bytes > 0){
|
||||
label += ' — ' + Math.round(p.percent) + '% (' + p.done_human + ' / ' + p.total_human + ')';
|
||||
bar.style.width = Math.max(0, Math.min(100, p.percent)) + '%';
|
||||
} else {
|
||||
label += ' — a mentendő adatok felmérése…';
|
||||
bar.style.width = '0%';
|
||||
}
|
||||
text.textContent = label;
|
||||
box.style.display = '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Not running. Terminal only if we ever saw it running — otherwise this is the pre-start
|
||||
race (or a page nobody triggered anything from). */
|
||||
if(sawRunning){ clearInterval(timer); location.reload(); return; }
|
||||
if(++quiet >= GRACE){ clearInterval(timer); }
|
||||
})
|
||||
.catch(function(){ /* transient — keep polling */ });
|
||||
}, 3000);
|
||||
|
||||
Reference in New Issue
Block a user