0fe315b759
gates / gates (push) Successful in 15s
MinAgent: 0.131.0 (unchanged). Requires hub v0.117.0 for restore_interrupted. R-550 (operator ruling: fix). A design reversed and recorded: the restore op-status was in memory by choice. Now restore-status.json in DataDir, written atomically at both ends of an op. At startup a record still marked running becomes a failed, interrupted result kept per app until that app's next restore, shown on /backups/restore and the off-site wizard, and raised once as restore_interrupted. Cooldowns stay in memory. R-546. The R-543 reminder bar consults the agent's own preflight ok (every blocking item, not a copy of pbs_storage_id), cached 60 s, probed only while paused. /backup/escrow shows a waiting card that polls and reloads instead of red crosses and English diagnostics. POST /api/escrow/start refuses 409 before staging or starting - the direct path chaos night used. Unknown readiness keeps the bar. Red-proofs (each seen failing): restore record across restart; main() calls both startup functions; startup helper with loading skipped; restore page card; bar held back; waiting card; start refusal. go build/vet/test ./... green, 28 packages; controller_gates --fast all OK. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
180 lines
11 KiB
HTML
180 lines
11 KiB
HTML
{{define "backups_restore_wizard"}}
|
|
{{template "layout_start" .}}
|
|
|
|
<!-- R-48: the offsite restore wizard. One entry per app on /backups/restore leads here, and the
|
|
three intents are separately-described CARDS with a consequence sentence each — never sibling
|
|
buttons whose difference is whether the data comes back. Server-rendered throughout: every step
|
|
is a real form POST to the EXISTING endpoint, so the page works with JavaScript disabled. -->
|
|
|
|
<div class="page-header">
|
|
<div style="display:flex;align-items:center;gap:.5rem">
|
|
<a href="/backups/restore" class="btn btn-sm btn-outline">← Vissza</a>
|
|
<h2>Visszaállítás — {{.AppDisplayName}}</h2>
|
|
</div>
|
|
<span class="domain-badge">{{.Domain}}</span>
|
|
</div>
|
|
|
|
{{template "backups_flash" .}}
|
|
{{template "restore_banner" .}}
|
|
|
|
<!-- Phase strip: the customer can see there IS a sequence, and where they are in it. The round-2
|
|
incident's second half was that the decisive step appeared only after the first was pressed,
|
|
with nothing signposting that a second step existed at all. -->
|
|
{{$phase := printf "%s" .Wizard.Phase}}
|
|
<div class="restore-wizard-phases">
|
|
<span class="restore-wizard-phase{{if eq $phase "elokeszites"}} is-current{{end}}">Előkészítés</span>
|
|
<span class="restore-wizard-phase{{if eq $phase "megerosites"}} is-current{{end}}">Megerősítés</span>
|
|
<span class="restore-wizard-phase{{if eq $phase "vegrehajtas"}} is-current{{end}}">Végrehajtás</span>
|
|
<span class="restore-wizard-phase{{if eq $phase "eredmeny"}} is-current{{end}}">Eredmény</span>
|
|
</div>
|
|
|
|
{{with .LastResult}}
|
|
<!-- „Eredmény": the outcome of the restore that just finished, for THIS app. The redirect flash says
|
|
the same thing but does not survive a reload; this does, for restoreResultWindow. -->
|
|
<div class="settings-card">
|
|
<h3>Eredmény</h3>
|
|
<div class="alert {{if .OK}}alert-info{{else}}alert-error{{end}}">{{.Message}}</div>
|
|
<p class="form-hint">{{if .Interrupted}}Észlelve{{else}}Befejezve{{end}}: {{fmtTime .FinishedAt}}. Ha szeretnéd, alább újra indíthatsz egy visszaállítást.</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if eq (printf "%s" .Wizard.Step) "execution"}}
|
|
<!-- EXECUTION — every mutation form is suppressed server-side. The manager's single-flight would
|
|
refuse them anyway; offering a control guaranteed to fail is the same dishonesty class R-48
|
|
addresses. The live progress comes from the existing restore-status poll (the banner above). -->
|
|
<div class="settings-card">
|
|
<h3>Végrehajtás</h3>
|
|
<p>Jelenleg egy mentési vagy visszaállítási művelet fut{{with .RunningStack}} ({{.}}){{end}}. Amíg ez tart, új visszaállítás nem indítható.</p>
|
|
<!-- R-351: this used to say the state refreshes automatically AND that you must refresh the page
|
|
when it finishes. Both cannot be true, and the second half was the one people believed. The
|
|
banner now carries the terminal result too (RestoreOpStatus.LastRecent), so the sentence can
|
|
describe what the page actually does. -->
|
|
<p class="form-hint">Az állapot fent automatikusan frissül, és a művelet eredménye is ott jelenik meg, amint elkészült.</p>
|
|
<div class="form-actions">
|
|
<a href="/backups/restore/app?name={{.App}}" class="btn btn-sm btn-outline">Állapot frissítése</a>
|
|
</div>
|
|
</div>
|
|
|
|
{{else if eq (printf "%s" .Wizard.Step) "prepare-confirm"}}
|
|
<!-- MEGERŐSÍTÉS — the size gate. The preparation has measured what needs downloading; the customer
|
|
confirms with the size in front of them, before any transfer starts. -->
|
|
<div class="settings-card">
|
|
<h3>Megerősítés — teljes visszaállítás előkészítése</h3>
|
|
<p>A teljes visszaállításhoz a mentés teljes tartalmát le kell tölteni a távoli tárolóból{{with .FullPrepSize}} — a becsült méret: <strong>{{.}}</strong>{{end}}. A letöltés a meghajtón egy külön előkészítő mappába kerül; az élő adataid ebben a lépésben még nem változnak.</p>
|
|
<p class="form-hint">A letöltés a mérettől és a kapcsolat sebességétől függően hosszabb ideig is tarthat. Ha elkészült, ezen az oldalon választhatod ki, hogy csak a hiányzó fájlokat hozod vissza, vagy teljes visszaállítást kérsz.</p>
|
|
<div class="form-actions">
|
|
<form method="POST" action="/backup/offbox/restore">{{.CSRFField}}
|
|
<input type="hidden" name="app" value="{{.App}}">
|
|
<input type="hidden" name="mode" value="full">
|
|
<input type="hidden" name="confirm" value="1">
|
|
<button type="submit" class="btn btn-primary">Előkészítés indítása{{with .FullPrepSize}} (~{{.}}){{end}}</button>
|
|
</form>
|
|
<a href="/backups/restore/app?name={{.App}}" class="btn btn-outline">Mégsem</a>
|
|
</div>
|
|
</div>
|
|
|
|
{{else}}
|
|
<!-- INTENT — three cards, each with its own consequence sentence. Card order is deliberate:
|
|
harmless first, irreversible-looking last. -->
|
|
|
|
<div class="settings-card">
|
|
<h3>1. Ellenőrzés külön mappába (beállítások és adatbázis)</h3>
|
|
<!-- R-204 item 3: this card's scope is stated BEFORE the choice, not only in the outcome. It
|
|
restores the recovery unit only; the customer's own files stay in the backup. Saying „a
|
|
mentés tartalma" here was how a disaster-recovery customer chose the one intent that does
|
|
not return their documents. -->
|
|
<p>Az alkalmazás beállításait és adatbázisát hozza vissza egy külön ellenőrző mappába. A saját fájljaidat (dokumentumok, képek, feltöltések) <strong>nem</strong> hozza vissza — azokhoz a 3. pont teljes visszaállítása kell. Az élő adataid nem változnak.</p>
|
|
<div class="form-actions">
|
|
<form method="POST" action="/backup/offbox/restore">{{.CSRFField}}
|
|
<input type="hidden" name="app" value="{{.App}}">
|
|
<input type="hidden" name="mode" value="unit">
|
|
<button type="submit" class="btn btn-outline"{{if not .Wizard.VerifyEnabled}} disabled{{end}}>Ellenőrzés indítása</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<h3>2. Hiányzó fájlok visszahozása</h3>
|
|
<p>Csak a hiányzó fájlokat másolja vissza a meglévők közé. A meglévő fájlokat nem írja felül, adatbázist nem állít vissza — törölt tartalom ettől nem jelenik meg újra.</p>
|
|
{{if .Wizard.PlaceEnabled}}
|
|
<div class="form-actions">
|
|
<form method="POST" action="/backup/offbox/place">{{.CSRFField}}
|
|
<input type="hidden" name="app" value="{{.App}}">
|
|
<button type="submit" class="btn btn-outline">Hiányzó fájlok visszahozása</button>
|
|
</form>
|
|
</div>
|
|
{{else}}
|
|
<p class="form-hint">Ehhez előbb elő kell készíteni a teljes mentést — lásd a 3. pontot.</p>
|
|
{{end}}
|
|
</div>
|
|
|
|
<div class="settings-card restore-danger-card">
|
|
<h3>3. Teljes visszaállítás (fájlok + adatbázis)</h3>
|
|
<p>A fájlokat a mentés szerinti változatra állítja vissza és az adatbázist is visszatölti. Semmit nem töröl: a mentés óta létrejött fájlok megmaradnak. A jelenlegi adatbázisról előtte biztonsági mentés készül.</p>
|
|
|
|
{{if .Wizard.RestoreEnabled}}
|
|
<!-- Pair honesty (R-43): what the two halves of this restore actually ARE. A restore is the one
|
|
operation whose result cannot be inspected before committing to it. -->
|
|
<div class="alert alert-info" style="margin-bottom:.75rem">
|
|
<strong>Az előkészített mentés:</strong>
|
|
{{if not .Pair.DumpsAt.IsZero}}adatbázis-mentés ideje: {{fmtTime .Pair.DumpsAt}}.{{else}}az adatbázis-mentés ideje nem állapítható meg.{{end}}
|
|
</div>
|
|
{{if .Pair.Skewed}}
|
|
<div class="alert alert-warning" style="margin-bottom:.75rem">Az adatbázis-mentés régebbi{{if not .Pair.DumpsAt.IsZero}} ({{fmtTime .Pair.DumpsAt}}){{end}} — a fájlok és az adatbázis eltérő időpontból származnak.</div>
|
|
{{end}}
|
|
{{if .Pair.LooksEmpty}}
|
|
<div class="alert alert-warning" style="margin-bottom:.75rem">A mentett adatbázis üresnek tűnik (nincs benne felhasználói fiók) — elképzelhető, hogy a mentés korábbi, mint az adataid.</div>
|
|
{{end}}
|
|
<div class="form-actions">
|
|
<form method="POST" action="/backup/offbox/reconstitute">{{.CSRFField}}
|
|
<input type="hidden" name="app" value="{{.App}}">
|
|
<input type="hidden" name="confirm" value="1">
|
|
<button type="button" class="btn btn-danger"
|
|
data-restore-app="{{.App}}"
|
|
data-restore-when="{{if not .Pair.DumpsAt.IsZero}}{{fmtTime .Pair.DumpsAt}}{{end}}"
|
|
data-restore-skewed="{{if .Pair.Skewed}}1{{end}}"
|
|
data-restore-empty="{{if .Pair.LooksEmpty}}1{{end}}"
|
|
onclick="confirmFullRestore(this)">Teljes visszaállítás indítása</button>
|
|
</form>
|
|
</div>
|
|
{{else}}
|
|
<p class="form-hint">A teljes visszaállításhoz először le kell tölteni a mentés teljes tartalmát. Az előkészítés megmutatja a méretet, mielőtt bármi elindulna — az élő adataid az előkészítés alatt nem változnak.</p>
|
|
<div class="form-actions">
|
|
<form method="POST" action="/backup/offbox/restore">{{.CSRFField}}
|
|
<input type="hidden" name="app" value="{{.App}}">
|
|
<input type="hidden" name="mode" value="full">
|
|
<button type="submit" class="btn btn-outline"{{if not .Wizard.PrepareEnabled}} disabled{{end}}>Teljes visszaállítás előkészítése</button>
|
|
</form>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
|
|
<script>
|
|
{{template "restore_banner_js"}}
|
|
|
|
/* Carried VERBATIM from backups_restore.html (R-43). The double-confirm is the good part of the old
|
|
surface — it states the DB half's age and any warning before the customer commits — so it moves
|
|
with the action rather than being rewritten. felhomConfirm is the house inline idiom, never the
|
|
OS-modal confirm() (F-11). */
|
|
function confirmFullRestore(btn){
|
|
var app = btn.getAttribute('data-restore-app') || '';
|
|
var when = btn.getAttribute('data-restore-when') || '';
|
|
var skewed = btn.getAttribute('data-restore-skewed') === '1';
|
|
var empty = btn.getAttribute('data-restore-empty') === '1';
|
|
var q = 'Teljes visszaállítás: ' + app + (when ? ' — a mentés ideje: ' + when : '') + '.';
|
|
if (skewed) { q += ' FIGYELEM: a fájlok és az adatbázis eltérő időpontból származnak.'; }
|
|
if (empty) { q += ' FIGYELEM: a mentett adatbázis üresnek tűnik.'; }
|
|
q += ' A fájlok a mentés szerinti változatra állnak vissza, semmi nem törlődik.';
|
|
felhomConfirm(btn, q, function(){
|
|
felhomConfirm(btn, 'UTOLSÓ MEGERŐSÍTÉS: az alkalmazás leáll, az adatbázis visszatöltődik, majd újraindul. A jelenlegi adatbázisról biztonsági mentés készül.', function(){
|
|
var f = btn.closest('form');
|
|
if (f) { if (f.requestSubmit) f.requestSubmit(); else f.submit(); }
|
|
});
|
|
});
|
|
}
|
|
</script>
|
|
|
|
{{template "layout_end" .}}
|
|
{{end}}
|