Files
felhom-controller/controller/internal/web/templates/backups_remote.html
T
admin 596505ed64 v0.142.0: offsite repo continuity — orphaned-repo guard (A) + run-status auto-refresh (C)
- Part A: classify restic cat-config failure (wrong-password=orphaned vs no-repo vs other); ORPHANED state + Hungarian card + offbox_repo_orphaned/reset events (once, not nightly); reset = move-aside (never delete) + init, unclaimed auto / claimed confirm. Red-proofs TestOffbox_OrphanDetection_* + ConfirmedReset.
- Part C: GET /backup/offbox/status + poll on backups_remote → flips Fut→Rendben/Hiba without manual reload.
2026-07-17 10:47:30 +02:00

196 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "backups_remote"}}
{{template "layout_start" .}}
<div class="page-header">
<h2>Biztonsági mentés — Távoli mentés</h2>
<span class="domain-badge">{{.Domain}}</span>
</div>
{{template "backups_flash" .}}
{{if not .Backup}}
{{template "backups_empty" .}}
{{else}}
<!-- Felhom-offsite status card (v0.124.0, decision 2): three honest states from LOCAL data only —
display + opt-in pointers, NEVER a state change (no form, no button in this card). State 3
(applied + apps toggled) is the existing status block below; the card renders nothing then. -->
{{if not .OffboxConfigured}}
<div class="alert alert-info" style="margin-bottom:1rem" id="felhom-offsite-card">Felhom offsite tárhely — igényelhető szolgáltatás. Titkosított, házon kívüli másolat a mentéseidről. Érdeklődj az üzemeltetőnél.</div>
{{else if eq .OffboxToggledCount 0}}
<div class="alert alert-info" style="margin-bottom:1rem" id="felhom-offsite-card">Aktív — nincs kijelölt alkalmazás</div>
{{end}}
<!-- Off-box (NAS) backup — Part B: encrypted restic repo over SFTP (the off-site 3-2-1 leg). -->
<h3 id="offbox-section" class="backup-tier-divider">Távoli mentés (3. mentés) — titkosított, offsite</h3>
<p class="form-hint" style="margin:-0.25rem 0 1rem">Az alkalmazás-mentések titkosított másolata egy távoli tárolóra — saját NAS vagy Felhom offsite tárhely — restic + SFTP kapcsolaton. A tároló csak titkosított adatot lát. Ez a 3-2-1 szabály „1 off-site" lába — független a helyi másodpéldánytól és a teljes rendszermentéstől.</p>
<div class="card" style="margin-bottom:1.5rem">
{{if .Offbox}}
<div class="stats-grid backup-page-cards">
<div class="stat-card {{if eq .Offbox.LastStatus "error"}}stat-warn{{end}}">
<div class="stat-value" id="offbox-status-value" style="font-size:1.15rem">{{if eq .Offbox.LastStatus "ok"}}&#10003; Rendben{{else if eq .Offbox.LastStatus "error"}}&#10007; Hiba{{else if eq .Offbox.LastStatus "running"}}Fut…{{else}}{{end}}</div>
<div class="stat-label">Utolsó távoli mentés{{if .Offbox.LastRun}}<br><span class="relative-time">{{timeAgoStr .Offbox.LastRun}}</span>{{end}}</div>
</div>
<div class="stat-card">
<div class="stat-value" style="font-size:1.15rem">{{if .Offbox.RepoSizeHuman}}{{.Offbox.RepoSizeHuman}}{{else}}{{end}}</div>
<div class="stat-label">Tároló méret · {{.Offbox.SnapshotCount}} pillanatkép</div>
</div>
<div class="stat-card">
<div class="stat-value" style="font-size:1.05rem">{{if .Offbox.Enabled}}{{.Offbox.User}}@{{.Offbox.Host}}{{else}}Kikapcsolva{{end}}</div>
<div class="stat-label">{{if .Offbox.Enabled}}{{.Offbox.RepoPath}}{{else}}A távoli mentés ki van kapcsolva{{end}}</div>
</div>
</div>
{{if and .Offbox.Enabled (gt .Offbox.QuotaGB 0)}}
<!-- SLICE 4: soft-quota usage bar (shared model; quota_gb from the hub descriptor). -->
<div id="offbox-quota-bar" style="max-width:560px;margin:.5rem 0">
<div class="stat-label" style="margin-bottom:.25rem">Tárhelykeret: {{if .Offbox.RepoSizeHuman}}{{.Offbox.RepoSizeHuman}}{{else}}0{{end}} / {{.Offbox.QuotaGB}} GB ({{.OffboxQuotaPct}}%)</div>
<div style="background:var(--border,#334);border-radius:4px;height:8px;overflow:hidden">
<div style="height:8px;border-radius:4px;width:{{.OffboxQuotaPct}}%;background:{{if ge .OffboxQuotaPct 100}}var(--crit,#e5484d){{else if ge .OffboxQuotaPct 80}}var(--warn,#f5a524){{else}}var(--ok,#30a46c){{end}}"></div>
</div>
</div>
{{end}}
{{if .Offbox.LastError}}<p class="form-hint" style="color:var(--crit)">Utolsó hiba: {{.Offbox.LastError}}</p>{{end}}
{{/* Part E: display pick — a stale zero-toggle warning is replaced once the selection
changed (neutral color: the replacement is reassurance, not a deviation). */}}
{{if .OffboxWarningDisplay}}<p class="form-hint"{{if eq .OffboxWarningDisplay .Offbox.LastWarning}} style="color:var(--warn)"{{end}}>{{.OffboxWarningDisplay}}</p>{{end}}
{{/* v0.142.0 offsite-repo continuity: the ORPHANED card replaces the raw restic error banner. The
remote holds backups written under a previous, no-longer-available key (reinstall shape). A
reset moves the old history aside (never deletes) and starts a fresh repo under the current key. */}}
{{if eq .Offbox.RepoState "orphaned"}}
<div class="card" style="border-left:3px solid var(--crit,#e5484d);margin:.75rem 0;padding:.75rem 1rem" id="offbox-orphan-card">
<p style="margin:0 0 .35rem;font-weight:600">A távoli tároló másik kulccsal készült mentéseket tartalmaz</p>
<p class="form-hint" style="margin:0 0 .5rem">A távoli tárhelyen lévő mentések egy korábbi, már nem elérhető kulccsal készültek (jellemzően újratelepítés után). Emiatt új mentés jelenleg nem írható a tárolóba. A meglévő mentések nem sérültek — a hozzájuk tartozó helyreállítási kóddal később visszaállíthatók lehetnek.</p>
<button type="button" class="btn btn-sm btn-outline" id="orphan-reveal" onclick="var c=document.getElementById('orphan-confirm');c.style.display='block';this.style.display='none'">Új távoli mentés indítása…</button>
<div id="orphan-confirm" style="display:none;margin-top:.6rem">
<p class="form-hint" style="margin:0 0 .5rem">A régi előzmény <strong>félretéve marad</strong> (nem törlődik), és a hozzá tartozó helyreállítási kóddal később visszaállítható lehet. Egy üres, új tároló jön létre a mostani kulccsal, és a következő mentés ide készül.</p>
<form method="POST" action="/backup/offbox/reset" style="display:inline">{{.CSRFField}}
<input type="hidden" name="confirm" value="1">
<button type="submit" class="btn btn-sm btn-primary">Megerősítés — új távoli mentés indítása</button>
</form>
</div>
</div>
{{end}}
{{/* Escrow ceremony card (v0.127.0): the customer-driveable wizard replaced the manual-confirm
button (that deprecated endpoint stays for legacy blobs; its button is gone). States:
awaiting (v0.138.0: ceremony done, hub confirm pending) → info; awaiting timed out → warn +
re-ceremony CTA; pending → CTA; escrowed+stale → warning + re-ceremony CTA; escrowed clean →
secondary link; agent too old → the honest version note, no CTA. */}}
{{if .OffboxCeremonyAwaiting}}
{{/* v0.138.0: the report-cycle gap between a completed ceremony and the hub-verified flip.
Info (blue) accent — NOT a warning: nothing is wrong, the confirmation is simply in flight. */}}
<div class="card" style="border-left:3px solid var(--blue);margin:.75rem 0;padding:.75rem 1rem">
<p style="margin:0 0 .35rem"><strong>Helyreállítási kód létrehozva</strong></p>
<p class="form-hint" style="margin:0">A helyreállítási csomag elküldve a központba, megerősítésre vár — ez általában néhány másodperc, legfeljebb 15 perc.</p>
</div>
{{else if .OffboxCeremonyTimedOut}}
{{/* v0.138.0: the confirmation never arrived within two report cycles + slack — degrade to a
warning with the re-ceremony CTA, never an indefinite "waiting". */}}
<div class="card" style="border-left:3px solid var(--warn);margin:.75rem 0;padding:.75rem 1rem">
<p class="form-hint" style="color:var(--warn);margin:0 0 .5rem">A megerősítés nem érkezett meg a várt időn belül. Ellenőrizze az internetkapcsolatot, majd készítsen új helyreállítási kódot.</p>
{{if .EscrowAgentOK}}
<a href="/backup/escrow" class="btn btn-sm btn-primary">Új helyreállítási kód készítése</a>
{{else}}
<p class="form-hint" style="margin:0">A funkcióhoz az ügynök frissítése szükséges — a frissítés automatikusan megérkezik.</p>
{{end}}
</div>
{{else if and .OffboxConfigured (ne .Offbox.EscrowState "escrowed")}}
<div class="card" style="border-left:3px solid var(--warn);margin:.75rem 0;padding:.75rem 1rem">
<p style="margin:0 0 .35rem"><strong>Helyreállítási kód szükséges</strong></p>
<p class="form-hint" style="margin:0 0 .5rem">A távoli mentések csak akkor állíthatók vissza egy teljes meghibásodás után, ha létrehozza a helyreállítási kódot.</p>
{{if .EscrowAgentOK}}
<a href="/backup/escrow" class="btn btn-sm btn-primary">Helyreállítási kód létrehozása</a>
{{else}}
<p class="form-hint" style="margin:0">A funkcióhoz az ügynök frissítése szükséges — a frissítés automatikusan megérkezik.</p>
{{end}}
</div>
{{else if and .OffboxConfigured .EscrowStale}}
<div class="card" style="border-left:3px solid var(--warn);margin:.75rem 0;padding:.75rem 1rem">
<p class="form-hint" style="color:var(--warn);margin:0 0 .5rem">A letétben lévő helyreállítási csomag nem fedi a jelenlegi távoli mentési jelszót. Hozzon létre új helyreállítási kódot.</p>
{{if .EscrowAgentOK}}
<a href="/backup/escrow" class="btn btn-sm btn-primary">Új helyreállítási kód készítése</a>
{{else}}
<p class="form-hint" style="margin:0">A funkcióhoz az ügynök frissítése szükséges — a frissítés automatikusan megérkezik.</p>
{{end}}
</div>
{{else if .OffboxConfigured}}
{{/* v0.127.1: the re-ceremony CTA is a real secondary button (outline, NOT primary — escrowed
is a healthy state; available-not-urgent, unlike the stale variant above). */}}
<div style="margin:.5rem 0">
<p class="form-hint" style="margin:0 0 .4rem">A helyreállítási kód letétbe helyezve.</p>
{{if .EscrowAgentOK}}<a href="/backup/escrow" class="btn btn-sm btn-outline">Új helyreállítási kód készítése</a>{{end}}
</div>
{{end}}
{{if .OffboxConfigured}}
<div class="schedule-actions" style="margin-top:1rem">
<form method="POST" action="/backup/offbox/run" style="display:inline">{{.CSRFField}}
<button type="submit" class="btn btn-sm btn-primary">Távoli mentés most</button>
</form>
</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>
{{end}}
{{if .OffboxApps}}
<div class="app-row-list">
{{range .OffboxApps}}
{{template "app_list_row" dict "Slug" .Slug "Name" .DisplayName}}
<form method="POST" action="/backup/offbox/toggle" style="display:inline">{{$.CSRFField}}
<input type="hidden" name="app" value="{{.Name}}">
<input type="hidden" name="enabled" value="{{if .Enabled}}false{{else}}true{{end}}">
<button type="submit" class="btn btn-xs {{if .Enabled}}btn-outline{{else}}btn-primary{{end}}">{{if .Enabled}}Távoli mentés kikapcsolása{{else}}Távoli mentés bekapcsolása{{end}}</button>
</form>
{{if $.OffboxBlockedSet}}{{if index $.OffboxBlockedSet .Name}}
<span class="form-hint" style="display:block;margin-top:.25rem">A teljes mentés túllépné a tárhelykeretet — csak a konfiguráció és az adatbázis kerül mentésre.</span>
{{end}}{{end}}
{{template "app_list_row_end"}}
{{end}}
</div>
{{else}}<p class="form-hint">Nincs telepített alkalmazás.</p>{{end}}
{{end}}
{{else}}
<p class="form-hint">Még nincs beállítva távoli mentési cél.</p>
{{end}}
<details style="margin-top:1rem">
<summary class="btn btn-xs btn-primary" style="cursor:pointer;display:inline-block">Távoli mentési cél beállítása</summary>
<form method="POST" action="/backup/offbox/config" style="margin-top:.75rem;padding:1rem;border:1px solid var(--border,#ddd);border-radius:6px;max-width:560px">{{.CSRFField}}
<div class="form-row"><label>Cél címe (IP vagy hosztnév — NAS vagy SFTP-kiszolgáló)</label><input type="text" name="host" class="form-input" value="{{if .Offbox}}{{.Offbox.Host}}{{end}}" placeholder="pl. 192.168.0.10"></div>
<div class="form-row"><label>SSH port</label><input type="number" name="port" class="form-input" value="{{if .Offbox}}{{.Offbox.Port}}{{else}}22{{end}}"></div>
<div class="form-row"><label>Felhasználó</label><input type="text" name="user" class="form-input" value="{{if .Offbox}}{{.Offbox.User}}{{end}}" placeholder="pl. felhom"></div>
<div class="form-row"><label>Tároló útvonala a célgépen</label><input type="text" name="repo_path" class="form-input" value="{{if .Offbox}}{{.Offbox.RepoPath}}{{end}}" placeholder="pl. /volume1/felhom-backup/repo"></div>
<div class="form-row"><label><input type="checkbox" name="enabled" {{if .Offbox}}{{if .Offbox.Enabled}}checked{{end}}{{else}}checked{{end}}> Napi automatikus távoli mentés engedélyezve</label></div>
<div class="form-row"><label>SSH privát kulcs {{if .OffboxConfigured}}(üresen hagyva változatlan){{end}}</label>
<textarea name="ssh_key" class="form-input" rows="4" autocomplete="off" placeholder="-----BEGIN OPENSSH PRIVATE KEY-----"></textarea>
<span class="form-hint">A kulcsot 0600-as fájlba írjuk; sosem naplózzuk és nem tároljuk a beállításokban.</span></div>
<div class="form-row"><label>A célgép ismert-host sora (known_hosts) {{if .OffboxConfigured}}(üresen hagyva változatlan){{end}}</label>
<textarea name="known_hosts" class="form-input" rows="2" autocomplete="off" placeholder="nas.local ssh-ed25519 AAAA…"></textarea>
<span class="form-hint">A host-kulcs rögzítése (no blind TOFU). Lekérdezhető: <span class="mono">ssh-keyscan -p &lt;port&gt; &lt;host&gt;</span></span></div>
<button type="submit" class="btn btn-sm btn-primary" style="margin-top:.5rem">Mentés</button>
</form>
</details>
</div>
{{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. */
(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(){
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)
})
.catch(function(){ /* transient — keep polling */ });
}, 3000);
})();
</script>
{{template "layout_end" .}}
{{end}}