2f8ff2414c
gates / gates (push) Successful in 17s
R-537 — the contents label is now PER TIER. One string computed from the app's shape was rendered on all three tier rows; a Tier-1 unit has no file-copy step, so for the four class-A apps it was claiming „Adatok" for files it does not hold. R-538 — a unit restore REFUSES before anything is touched when the unit cannot return the app's drive-side files, and names the route that can. It runs before the stack is stopped because the measured harm included the app's own wastebasket going unreachable, which still held every byte. R-536 — „Alkalmazás telepítve" moved from the deploy's acceptance to its completion, with app_deploy_started and app_deploy_failed as the honest pair. Each fix red-proofed: seen failing with its own sentence, passing when restored. Requires hub v0.116.0 for the two new event types. MinAgent unchanged (0.131.0). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
427 lines
25 KiB
HTML
427 lines
25 KiB
HTML
{{define "backups_apps"}}
|
||
{{template "layout_start" .}}
|
||
|
||
<div class="page-header">
|
||
<h2>Biztonsági mentés — Alkalmazások</h2>
|
||
<span class="domain-badge">{{.Domain}}</span>
|
||
</div>
|
||
|
||
{{template "backups_flash" .}}
|
||
{{template "restore_banner" .}}
|
||
|
||
{{if not .Backup}}
|
||
{{template "backups_empty" .}}
|
||
{{else}}
|
||
|
||
<h3 class="backup-tier-divider">Alkalmazás-mentések (adatbázis + konfiguráció)</h3>
|
||
<p class="form-hint" style="margin:-0.25rem 0 1rem">Az egyes alkalmazások részletes, granulált mentése — adatbázis-kiírások, beállítások és alkalmazás-fájlok. A fenti teljes mentéstől függetlenül, alkalmazásonként visszaállítható.</p>
|
||
|
||
<!-- Section 2: Schedule -->
|
||
<div class="schedule-card">
|
||
<h3>Ütemezés</h3>
|
||
<div class="schedule-rows">
|
||
<div class="schedule-row">
|
||
<span class="schedule-task">Adatbázis mentés</span>
|
||
<span class="schedule-time">{{.Backup.DBDumpSchedule}}</span>
|
||
<span class="schedule-next">Következő: {{nextRunLabel .Backup.NextDBDump}}</span>
|
||
</div>
|
||
</div>
|
||
<div class="schedule-summary">
|
||
{{if .Backup.LastDBDump}}
|
||
<div class="schedule-summary-row">
|
||
<span>Utolsó adatbázis mentés:</span>
|
||
<span class="schedule-summary-value">{{fmtTime .Backup.LastDBDump.LastRun}} ({{timeAgo .Backup.LastDBDump.LastRun}})</span>
|
||
</div>
|
||
{{else}}
|
||
<div class="schedule-summary-row">
|
||
<span>Utolsó adatbázis mentés:</span>
|
||
<span class="schedule-summary-value relative-time">Még nem futott</span>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
<div class="schedule-actions">
|
||
<button class="btn btn-sm btn-primary" onclick="triggerBackupFromPage()" id="backup-page-btn"
|
||
{{if .Backup.Running}}disabled{{end}}>
|
||
{{if .Backup.Running}}Mentés folyamatban...{{else}}Mentés most{{end}}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Section 3: Databases -->
|
||
<div class="backup-section-card">
|
||
<h3>Adatbázisok</h3>
|
||
{{if or .Backup.DumpFiles .Backup.DiscoveredDBs}}
|
||
<div class="backup-table-wrap">
|
||
<table class="db-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Alkalmazás</th>
|
||
<th>Típus</th>
|
||
<th>Méret</th>
|
||
<th>Utolsó</th>
|
||
<th>Érvényesítés</th>
|
||
<th>Állapot</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{{if .Backup.LastDBDump}}
|
||
{{range .Backup.LastDBDump.Results}}
|
||
<tr>
|
||
<td>{{.DB.StackName}}</td>
|
||
<td><span class="db-type-badge db-type-{{.DB.DBType}}">{{dbTypeLabel .DB.DBType}}</span></td>
|
||
<td class="mono">{{if .Error}}–{{else}}{{fmtBytes .Size}}{{end}}</td>
|
||
<td class="mono">{{if .Error}}–{{else}}{{fmtTimeShort $.Backup.LastDBDump.LastRun}}{{end}}</td>
|
||
<td>
|
||
{{if .Error}}
|
||
<span class="validation-badge validation-na">–</span>
|
||
{{else if .Validation.Valid}}
|
||
<span class="validation-badge validation-ok">{{.Validation.TableCount}} tábla</span>
|
||
{{else if .Validation.Error}}
|
||
<span class="validation-badge validation-fail" title="{{.Validation.Error}}">Hiba</span>
|
||
{{else}}
|
||
<span class="validation-badge validation-na" title="Az érvényesítés nem futott le">–</span>
|
||
{{end}}
|
||
</td>
|
||
<td>
|
||
{{if .Error}}
|
||
<span class="validation-badge validation-fail" title="{{.Error}}">Hiba</span>
|
||
{{else}}
|
||
<span class="validation-badge validation-ok">OK</span>
|
||
{{end}}
|
||
</td>
|
||
</tr>
|
||
{{end}}
|
||
{{else}}
|
||
{{range .Backup.DumpFiles}}
|
||
<tr>
|
||
<td>{{.StackName}}</td>
|
||
<td><span class="db-type-badge db-type-{{.DBType}}">{{dbTypeLabel .DBType}}</span></td>
|
||
<td class="mono">{{fmtBytes .Size}}</td>
|
||
<td class="mono">{{fmtTimeShort .ModTime}}</td>
|
||
<td>
|
||
{{if .Validation.Valid}}
|
||
<span class="validation-badge validation-ok">{{.Validation.TableCount}} tábla</span>
|
||
{{else if .Validation.Error}}
|
||
<span class="validation-badge validation-fail" title="{{.Validation.Error}}">Hiba</span>
|
||
{{else}}
|
||
<span class="validation-badge validation-na">–</span>
|
||
{{end}}
|
||
</td>
|
||
<td><span class="validation-badge validation-ok">OK</span></td>
|
||
</tr>
|
||
{{end}}
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{{else if eq .DBSectionState "pending"}}
|
||
<div class="backup-table-empty">Még nem készült adatbázis-mentés — az első ütemezett mentés éjjel fut le.</div>
|
||
{{else}}
|
||
<div class="backup-table-empty">A telepített alkalmazások beágyazott adatbázist használnak (pl. SQLite) — adatbázisuk a fájl- és kötetmentés része, külön adatbázis-kiírás nem szükséges.</div>
|
||
{{end}}
|
||
</div>
|
||
|
||
<!-- Section 4: Unified per-app backup status -->
|
||
{{if .Backup.AppDataInfo}}
|
||
<div class="backup-section-card">
|
||
<h3>Alkalmazások mentési állapota</h3>
|
||
|
||
{{if .NoUserDataBackupWarning}}
|
||
<div class="alert alert-error" style="margin-bottom:1.5rem">
|
||
<strong>Felhasználói adatokról nincs biztonsági mentés.</strong><br>
|
||
A szerveren tárolt fotók, dokumentumok és egyéb fájlok jelenleg csak egy példányban léteznek.
|
||
Külső meghajtó csatlakoztatásával biztonsági másolat készíthető a 3-2-1 szabály szerint.
|
||
<a href="/settings" style="color:inherit;text-decoration:underline">Meghajtó beállítása →</a>
|
||
</div>
|
||
{{end}}
|
||
|
||
{{range .AppBackupRows}}
|
||
<div class="app-backup-row" data-status="{{.Status}}">
|
||
<!-- Aligned to the shared app-row grammar (icon + name left, status + chevron right);
|
||
the expander behavior is untouched — this header is the one allowlisted aligned
|
||
copy in scripts/app_row_dedup_gate.py, NOT a partial render (it owns the toggle).
|
||
The icon chain matches app_row.html: SVG -> PNG -> /static/app-placeholder.svg, so a
|
||
logo-less app shows the placeholder here too instead of a hidden dead-end (v0.163.1). -->
|
||
<div class="app-backup-row-header" onclick="toggleBackupDetail(this)">
|
||
<img class="app-row-icon" src="{{logoURL .Slug}}" alt="" data-fallback="/static/app-placeholder.svg"
|
||
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='{{logoPNGURL .Slug}}';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
||
<span class="app-backup-row-name">{{.DisplayName}}</span>
|
||
<div class="app-backup-row-meta">
|
||
{{if .Removed}}
|
||
<span class="tag tag-warn">Eltávolítva — visszaállítható</span>
|
||
{{if .StorageLabel}}<span class="meta-badge meta-badge-storage">{{.StorageLabel}}</span>{{end}}
|
||
{{else if .DriveDisconnected}}
|
||
<span class="tag tag-warn">Meghajtó leválasztva</span>
|
||
{{else if .HasHDDData}}
|
||
{{if .StorageLabel}}<span class="meta-badge meta-badge-storage">{{.StorageLabel}}</span>{{end}}
|
||
<span class="mono app-backup-size" style="font-size:.8rem">{{.HDDSizeHuman}}</span>
|
||
{{else if .HasVolumeData}}
|
||
<span class="meta-badge">Konfig{{if .HasDB}} + DB{{end}} + Adatok</span>
|
||
{{else}}
|
||
<span class="meta-badge">Konfig{{if .HasDB}} + DB{{end}}</span>
|
||
{{end}}
|
||
<span class="status-dot status-{{.Status}}" title="{{.StatusText}}"></span>
|
||
</div>
|
||
<span class="expand-icon">▶</span>
|
||
</div>
|
||
<div class="app-backup-row-detail" style="display:none">
|
||
{{if .Removed}}
|
||
{{/* R-487: a removed app's kept unit. One action — the same restore the picker on the
|
||
Visszaállítás page runs (POST /backup/restore, snapshot „helyi"); it reinstalls the app
|
||
from the unit (R-253). No tier rows: nothing is scheduled for an app that is not here. */}}
|
||
<div class="backup-layers">
|
||
<div class="backup-layer-row">
|
||
<span class="tier-label">Mentés</span>
|
||
<span class="layer-badge">Megtartva</span>
|
||
<span class="tier-location">helyi</span>
|
||
<span class="tier-contents">Az alkalmazást eltávolítottad, a mentése megmaradt. Legutóbbi mentés: {{fmtTimeStr .RemovedUnitTime}}.</span>
|
||
<div class="layer-actions">
|
||
<form method="POST" action="/backup/restore" style="display:inline">{{$.CSRFField}}
|
||
<input type="hidden" name="stack_name" value="{{.StackName}}">
|
||
<input type="hidden" name="snapshot_id" value="helyi">
|
||
<button type="submit" class="btn btn-xs btn-outline" data-confirm="Újratelepíti a(z) {{.DisplayName}} alkalmazást a megtartott mentésből ({{fmtTimeStr .RemovedUnitTime}})? Az adatok a mentésből állnak vissza.">Visszaállítás a mentésből</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{else}}
|
||
<div class="backup-layers">
|
||
<!-- Tier 1: Nightly backup (mandatory, same drive) -->
|
||
<div class="backup-layer-row">
|
||
<span class="tier-label">1. mentés</span>
|
||
<span class="layer-badge">Auto</span>
|
||
<span class="tier-location">helyi</span>
|
||
{{if .Tier1LastRun}}
|
||
{{/* R-258: three states. The tick is THIS app's own most recent dump result,
|
||
not the box's most recent run — and a third value renders NO icon, which is
|
||
where "we have no result for this backup" belongs. A tick standing for
|
||
"a file exists" is what this replaced. */}}
|
||
<span class="layer-last">Utolsó: {{timeAgoStr .Tier1LastRun}}
|
||
{{if eq .Tier1LastStatus "ok"}}<span class="text-ok"><svg class="ico ico-sm"><use href="#i-check"/></svg></span>
|
||
{{else if eq .Tier1LastStatus "error"}}<span class="text-error"><svg class="ico ico-sm"><use href="#i-x"/></svg></span>
|
||
{{else}}<span class="state-text-neutral" title="Erről a mentésről nincs eredményünk.">—</span>{{end}}
|
||
</span>
|
||
{{end}}
|
||
<span class="tier-contents">{{.Tier1Contents}}</span>
|
||
{{if .DriveFilesNote}}<span class="state-text-neutral" style="font-size:.8rem">{{.DriveFilesNote}}</span>{{end}}
|
||
{{if and .HasDB (eq .Tier1DBStatus "error")}}
|
||
<span class="text-error" style="font-size:.8rem"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> DB dump hiba</span>
|
||
{{end}}
|
||
</div>
|
||
<!-- Tier 2: Cross-drive backup (opt-in, different device) -->
|
||
<div class="backup-layer-row">
|
||
<span class="tier-label">2. mentés</span>
|
||
{{if .Tier2UserDisabled}}
|
||
<span class="layer-unconfigured">2. mentés kikapcsolva</span>
|
||
<div class="layer-actions">
|
||
<a href="/stacks/{{.StackName}}/backup" class="btn btn-xs btn-outline">Beállítás</a>
|
||
</div>
|
||
{{else if and .Tier2Configured .Tier2DestDisconnected}}
|
||
<span class="layer-method" style="opacity:.6">rsync</span>
|
||
<span class="layer-dest" style="opacity:.6">→ {{.Tier2Dest}}</span>
|
||
<span class="tag tag-warn">Cél meghajtó leválasztva</span>
|
||
{{if not .Tier2SuccessTracked}}{{if .Tier2LastRun}}
|
||
<span class="layer-last" style="opacity:.6">Utolsó: {{timeAgoStr .Tier2LastRun}}</span>
|
||
{{end}}{{else if .Tier2LastSuccess}}
|
||
<span class="layer-last" style="opacity:.6">Utolsó sikeres: {{timeAgoStr .Tier2LastSuccess}}</span>
|
||
{{else}}
|
||
<span class="layer-last" style="opacity:.6">Még nincs sikeres másolat</span>
|
||
{{end}}
|
||
<span class="tier-contents" style="opacity:.6">{{.Tier23Contents}}</span>
|
||
<div class="layer-actions">
|
||
<a href="/stacks/{{.StackName}}/backup" class="btn btn-xs btn-outline">Beállítás</a>
|
||
</div>
|
||
{{else if and .Tier2Configured .Tier2DestInactive}}
|
||
<span class="layer-method" style="opacity:.6">rsync</span>
|
||
<span class="layer-dest" style="opacity:.6">→ {{.Tier2Dest}}</span>
|
||
<span class="tag tag-warn">Cél meghajtó inaktív</span>
|
||
{{if not .Tier2SuccessTracked}}{{if .Tier2LastRun}}
|
||
<span class="layer-last" style="opacity:.6">Utolsó: {{timeAgoStr .Tier2LastRun}}</span>
|
||
{{end}}{{else if .Tier2LastSuccess}}
|
||
<span class="layer-last" style="opacity:.6">Utolsó sikeres: {{timeAgoStr .Tier2LastSuccess}}</span>
|
||
{{else}}
|
||
<span class="layer-last" style="opacity:.6">Még nincs sikeres másolat</span>
|
||
{{end}}
|
||
<span class="tier-contents" style="opacity:.6">{{.Tier23Contents}}</span>
|
||
<div class="layer-actions">
|
||
<a href="/stacks/{{.StackName}}/backup" class="btn btn-xs btn-outline">Beállítás</a>
|
||
</div>
|
||
{{else if .Tier2Configured}}
|
||
<span class="layer-method">rsync</span>
|
||
<span class="layer-dest">→ {{.Tier2Dest}}</span>
|
||
<span class="layer-schedule">{{.Tier2Schedule}}</span>
|
||
{{if not .Tier2SuccessTracked}}{{if .Tier2LastRun}}
|
||
<span class="layer-last">Utolsó: {{timeAgoStr .Tier2LastRun}}
|
||
<span class="{{if eq .Tier2LastStatus "ok"}}text-ok{{else if eq .Tier2LastStatus "error"}}text-error{{else if eq .Tier2LastStatus "running"}}text-muted{{end}}">
|
||
{{.Tier2StatusBadge}}
|
||
</span>
|
||
</span>
|
||
{{end}}{{else if .Tier2LastSuccess}}
|
||
<span class="layer-last">Utolsó sikeres: {{timeAgoStr .Tier2LastSuccess}}
|
||
<span class="{{if eq .Tier2LastStatus "ok"}}text-ok{{else if eq .Tier2LastStatus "error"}}text-error{{else if eq .Tier2LastStatus "running"}}text-muted{{end}}">
|
||
{{.Tier2StatusBadge}}
|
||
</span>
|
||
</span>
|
||
{{else}}
|
||
<span class="layer-last">Még nincs sikeres másolat
|
||
<span class="{{if eq .Tier2LastStatus "error"}}text-error{{else}}text-muted{{end}}">{{.Tier2StatusBadge}}</span>
|
||
</span>
|
||
{{end}}
|
||
{{if .Tier2SizeHuman}}<span class="tier-size">{{.Tier2SizeHuman}}</span>{{end}}
|
||
{{if .Tier2LastWarning}}<span class="layer-reason" style="color:var(--warn);opacity:.9">{{.Tier2LastWarning}}</span>{{end}}
|
||
{{/* R-403: a run that PRESERVED the copy's package instead of refreshing it must
|
||
not render as a plain fresh copy. The status line above is about the RUN;
|
||
this one is about the PACKAGE, and after a preserved leg they differ. */}}
|
||
{{if .Tier2UnitStaleNotice}}<span class="layer-reason" style="color:var(--warn);opacity:.9">{{.Tier2UnitStaleNotice}}</span>{{end}}
|
||
<span class="tier-contents">{{.Tier23Contents}}</span>
|
||
<span class="tier-browsable" title="A mentés böngészhető fájlrendszerben"><svg class="ico ico-sm"><use href="#i-file-text"/></svg></span>
|
||
<div class="layer-actions">
|
||
{{if not .Tier2SuccessTracked}}{{if .Tier2LastRun}}
|
||
<form method="POST" action="/backup/tier2/restore" style="display:inline">{{$.CSRFField}}
|
||
<input type="hidden" name="stack_name" value="{{.StackName}}">
|
||
<button type="submit" class="btn btn-xs btn-outline" data-confirm="Visszaállítja a hiányzó fájlokat a másodlagos másolatból? A meglévő fájlok NEM módosulnak és NEM törlődnek. Az alkalmazás a művelet idejére leáll. Legutóbbi másolat: {{fmtTimeStr .Tier2LastRun}}">Fájlok visszaállítása</button>
|
||
</form>
|
||
{{end}}{{else if .Tier2LastSuccess}}
|
||
<form method="POST" action="/backup/tier2/restore" style="display:inline">{{$.CSRFField}}
|
||
<input type="hidden" name="stack_name" value="{{.StackName}}">
|
||
<button type="submit" class="btn btn-xs btn-outline" data-confirm="Visszaállítja a hiányzó fájlokat a másodlagos másolatból? A meglévő fájlok NEM módosulnak és NEM törlődnek. Az alkalmazás a művelet idejére leáll. Legutóbbi sikeres másolat: {{fmtTimeStr .Tier2LastSuccess}}.{{if .Tier2StaleCopy}} Figyelem: a legutóbbi mentési kísérlet nem sikerült, ezért a visszaállított fájlok ennél régebbiek lehetnek.{{end}}">Fájlok visszaállítása</button>
|
||
</form>
|
||
{{else}}
|
||
<span class="layer-reason" style="opacity:.85">Még nincs sikeres másolat, amiből vissza lehetne állítani.</span>
|
||
{{end}}
|
||
{{/* R-102/R-103: the DESTRUCTIVE twin of the button above. Offered only when the
|
||
copy holds an openable recovery unit (Tier2UnitRestorable — a second
|
||
predicate, never a widening of the file restore's). It is deliberately a
|
||
SEPARATE button in a danger style: the one beside it only adds what is
|
||
missing, this one overwrites the app's live data with the copy's. */}}
|
||
{{if .Tier2UnitRestorable}}
|
||
<form method="POST" action="/backup/tier2/unit-restore" style="display:inline">{{$.CSRFField}}
|
||
<input type="hidden" name="stack_name" value="{{.StackName}}">
|
||
<button type="submit" class="btn btn-xs btn-danger-outline" data-confirm="{{.Tier2UnitConfirm}}">Teljes visszaállítás a másolatból</button>
|
||
</form>
|
||
{{end}}
|
||
<a href="/stacks/{{.StackName}}/backup" class="btn btn-xs btn-outline">Beállítás</a>
|
||
</div>
|
||
{{else}}
|
||
<span class="layer-auto-ok"><svg class="ico ico-sm"><use href="#i-check"/></svg> 1. mentés auto</span>
|
||
<span class="layer-unconfigured"><svg class="ico ico-sm"><use href="#i-triangle-alert"/></svg> Nincs 2. (off-drive) másolat</span>
|
||
{{if .Tier2LastError}}
|
||
<span class="layer-reason" style="opacity:.85" title="A 2. mentés automatikus — külön beállítás nem kell">{{.Tier2LastError}}</span>
|
||
{{end}}
|
||
<div class="layer-actions">
|
||
<a href="/stacks/{{.StackName}}/backup" class="btn btn-xs btn-outline">Beállítás</a>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
<!-- Tier 3: Off-box (NAS) backup — real state (see tier3State). -->
|
||
{{if eq .Tier3State "active"}}
|
||
<div class="backup-layer-row">
|
||
<span class="tier-label">3. mentés</span>
|
||
<span class="layer-badge {{if eq $.Offbox.LastStatus "ok"}}text-ok{{else if eq $.Offbox.LastStatus "error"}}text-error{{else if eq $.Offbox.LastStatus "running"}}text-muted{{end}}">{{if eq $.Offbox.LastStatus "ok"}}Sikeres{{else if eq $.Offbox.LastStatus "error"}}Hiba{{else if eq $.Offbox.LastStatus "running"}}Fut...{{else}}—{{end}}</span>
|
||
<span class="tier-location">restic → {{$.Offbox.Host}}</span>
|
||
{{if $.Offbox.LastRun}}<span class="layer-last">Utolsó: {{timeAgoStr $.Offbox.LastRun}}</span>{{end}}
|
||
<span class="tier-contents">Helyreállítási egység, titkosítva</span>
|
||
</div>
|
||
{{else if eq .Tier3State "escrow_pending"}}
|
||
<div class="backup-layer-row">
|
||
<span class="tier-label">3. mentés</span>
|
||
<span class="layer-badge text-error">Kulcsletétre vár</span>
|
||
<span class="tier-contents">A távoli mentés a titkosítási kulcs letétbe helyezéséig szünetel</span>
|
||
</div>
|
||
{{else if eq .Tier3State "off"}}
|
||
<div class="backup-layer-row" style="opacity:.5">
|
||
<span class="tier-label">3. mentés</span>
|
||
<span class="layer-badge">Kikapcsolva</span>
|
||
<span class="tier-contents">Ez az alkalmazás nincs kijelölve távoli mentésre</span>
|
||
<div class="layer-actions">
|
||
<a href="/backups/remote#offbox-section" class="btn btn-xs btn-outline">Bekapcsolás</a>
|
||
</div>
|
||
</div>
|
||
{{else}}
|
||
<div class="backup-layer-row" style="opacity:.5">
|
||
<span class="tier-label">3. mentés</span>
|
||
<span class="layer-badge" style="background:var(--bg-2);color:var(--text-3)">Nincs beállítva</span>
|
||
<span class="tier-location">távoli (offsite)</span>
|
||
<span class="tier-contents">Nincs távoli mentési cél beállítva</span>
|
||
<div class="layer-actions">
|
||
<a href="/backups/remote#offbox-section" class="btn btn-xs btn-outline">Beállítás</a>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
{{if .Warnings}}
|
||
<div class="layer-warnings">
|
||
{{range .Warnings}}
|
||
<div class="backup-layer-warning">{{.}}</div>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
|
||
</div>
|
||
{{end}}
|
||
|
||
{{end}}
|
||
|
||
<script>
|
||
{{template "restore_banner_js"}}
|
||
function toggleBackupDetail(header) {
|
||
var detail = header.nextElementSibling;
|
||
var icon = header.querySelector('.expand-icon');
|
||
if (detail.style.display === 'none') {
|
||
detail.style.display = 'block';
|
||
icon.textContent = '▼';
|
||
} else {
|
||
detail.style.display = 'none';
|
||
icon.textContent = '▶';
|
||
}
|
||
}
|
||
|
||
function triggerBackupFromPage() {
|
||
const btn = document.getElementById('backup-page-btn');
|
||
btn.disabled = true;
|
||
btn.textContent = 'Mentés indítása...';
|
||
fetch('/api/backup/run', { method: 'POST', headers: csrfHeaders() })
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.ok) {
|
||
btn.textContent = 'Mentés folyamatban...';
|
||
btn.classList.add('loading');
|
||
startBackupPolling();
|
||
} else {
|
||
btn.textContent = data.error || 'Hiba';
|
||
setTimeout(() => { btn.textContent = 'Mentés most'; btn.disabled = false; }, 3000);
|
||
}
|
||
})
|
||
.catch(() => {
|
||
btn.textContent = 'Hiba';
|
||
setTimeout(() => { btn.textContent = 'Mentés most'; btn.disabled = false; }, 3000);
|
||
});
|
||
}
|
||
|
||
function startBackupPolling() {
|
||
const poll = setInterval(() => {
|
||
fetch('/api/backup/status')
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.ok && data.data && !data.data.running) {
|
||
clearInterval(poll);
|
||
window.location.reload();
|
||
}
|
||
})
|
||
.catch(() => {});
|
||
}, 3000);
|
||
}
|
||
|
||
// Auto-poll if backup is already running on page load
|
||
{{if .Backup}}{{if .Backup.Running}}
|
||
startBackupPolling();
|
||
{{end}}{{end}}
|
||
</script>
|
||
|
||
{{template "layout_end" .}}
|
||
{{end}}
|