2b01e09579
Replace the complex "Alkalmazás adatok" form (checkboxes, paths, volumes, save button) with a clean read-only status list. Each app shows its name (linked to its deploy page) and a simple status: Aktív / Inaktív / N/A. Also include ALL deployed stacks in the list (not just HDD-capable ones), so apps without user data appear with N/A status. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
627 lines
25 KiB
HTML
627 lines
25 KiB
HTML
{{define "backups"}}
|
||
{{template "layout_start" .}}
|
||
|
||
<div class="page-header">
|
||
<h2>Biztonsági mentés</h2>
|
||
<span class="domain-badge">{{.Domain}}</span>
|
||
</div>
|
||
|
||
{{if .Backup}}{{if .Backup.FlashSuccess}}
|
||
<div class="flash flash-success">{{.Backup.FlashSuccess}}</div>
|
||
{{end}}{{end}}
|
||
{{if .Backup}}{{if .Backup.FlashError}}
|
||
<div class="flash flash-error">{{.Backup.FlashError}}</div>
|
||
{{end}}{{end}}
|
||
|
||
{{if not .Backup}}
|
||
<div class="backup-empty-state">
|
||
<div class="backup-empty-icon">🛡</div>
|
||
<h3>Biztonsági mentés nincs beállítva</h3>
|
||
<p>A biztonsági mentés funkció nem aktív.<br>
|
||
Kérjük, vegye fel a kapcsolatot a Felhom csapattal a beállításhoz.</p>
|
||
</div>
|
||
{{else}}
|
||
|
||
<!-- Section 0: Storage overview -->
|
||
<div class="backup-section-card">
|
||
<h3>Tárhely áttekintés</h3>
|
||
<div class="storage-overview-grid">
|
||
<div class="storage-bars">
|
||
{{with $.SystemInfo}}
|
||
<div class="storage-item">
|
||
<div class="storage-header">
|
||
<span class="storage-label">SSD (/)</span>
|
||
<span class="storage-value">{{fmtGB .DiskUsedGB}} / {{fmtGB .DiskTotalGB}} ({{printf "%.0f" .DiskPercent}}%)</span>
|
||
</div>
|
||
<div class="system-bar">
|
||
<div class="system-bar-fill {{usageColor .DiskPercent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .DiskPercent}}%"></div>
|
||
</div>
|
||
</div>
|
||
{{if .HDDConfigured}}
|
||
<div class="storage-item">
|
||
<div class="storage-header">
|
||
<span class="storage-label">Külső HDD</span>
|
||
<span class="storage-value">{{fmtGB .HDDUsedGB}} / {{fmtGB .HDDTotalGB}} ({{printf "%.0f" .HDDPercent}}%)</span>
|
||
</div>
|
||
<div class="system-bar">
|
||
<div class="system-bar-fill {{usageColor .HDDPercent | printf "system-bar-%s"}}" style="width:{{printf "%.1f" .HDDPercent}}%"></div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
{{end}}
|
||
</div>
|
||
<div class="storage-stats">
|
||
{{if .Backup.RepoStats}}
|
||
<div class="storage-stat-row">
|
||
<span class="storage-stat-label">Mentési tároló</span>
|
||
<span class="storage-stat-value mono">{{.Backup.RepoStats.TotalSize}}</span>
|
||
</div>
|
||
{{end}}
|
||
<div class="storage-stat-row">
|
||
<span class="storage-stat-label">DB mentések</span>
|
||
<span class="storage-stat-value mono">{{if .Backup.DumpFiles}}{{len .Backup.DumpFiles}} fájl{{else}}–{{end}}</span>
|
||
</div>
|
||
<div class="storage-stat-row">
|
||
<span class="storage-stat-label">Pillanatképek</span>
|
||
<span class="storage-stat-value mono">{{if .Backup.RepoStats}}{{.Backup.RepoStats.SnapshotCount}}{{else}}0{{end}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Section 1: Status overview cards -->
|
||
<div class="stats-grid backup-page-cards">
|
||
{{if .Backup.LastBackup}}
|
||
{{if .Backup.LastBackup.Success}}
|
||
<div class="stat-card stat-running">
|
||
<div class="stat-value">✓</div>
|
||
<div class="stat-label">Helyi mentés aktív</div>
|
||
</div>
|
||
{{else}}
|
||
<div class="stat-card stat-stopped">
|
||
<div class="stat-value">✗</div>
|
||
<div class="stat-label">Helyi mentés sikertelen</div>
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<div class="stat-card">
|
||
<div class="stat-value">–</div>
|
||
<div class="stat-label">Helyi mentés</div>
|
||
</div>
|
||
{{end}}
|
||
|
||
<div class="stat-card" style="border-left-color: var(--gray);">
|
||
<div class="stat-value" style="background:var(--gray);-webkit-background-clip:text;background-clip:text;">–</div>
|
||
<div class="stat-label">Távoli mentés<br><span class="relative-time">nincs beállítva</span></div>
|
||
</div>
|
||
|
||
<div class="stat-card stat-total">
|
||
<div class="stat-value">
|
||
{{if .Backup.LastDBDump}}{{len .Backup.LastDBDump.Results}}{{else}}{{len .Backup.DumpFiles}}{{end}}
|
||
</div>
|
||
<div class="stat-label">Adatbázis mentve</div>
|
||
</div>
|
||
|
||
<div class="stat-card stat-total">
|
||
<div class="stat-value">
|
||
{{if .Backup.RepoStats}}{{.Backup.RepoStats.TotalSize}}{{else}}–{{end}}
|
||
</div>
|
||
<div class="stat-label">Tároló méret
|
||
{{if .Backup.RepoStats}}<br><span class="relative-time">{{.Backup.RepoStats.SnapshotCount}} pillanatkép</span>{{end}}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 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 class="schedule-row">
|
||
<span class="schedule-task">Restic pillanatkép</span>
|
||
<span class="schedule-time">{{.Backup.ResticSchedule}}</span>
|
||
<span class="schedule-next">Következő: {{nextRunLabel .Backup.NextBackup}}</span>
|
||
</div>
|
||
<div class="schedule-row">
|
||
<span class="schedule-task">Karbantartás</span>
|
||
<span class="schedule-time">{{pruneLabel .Backup.PruneSchedule}}</span>
|
||
<span class="schedule-next">Következő: {{nextPruneLabel .Backup.PruneSchedule}}</span>
|
||
</div>
|
||
</div>
|
||
<div class="schedule-summary">
|
||
{{if .Backup.LastBackup}}
|
||
<div class="schedule-summary-row">
|
||
<span>Utolsó sikeres mentés:</span>
|
||
<span class="schedule-summary-value">{{fmtTime .Backup.LastBackup.LastRun}} ({{timeAgo .Backup.LastBackup.LastRun}})</span>
|
||
</div>
|
||
<div class="schedule-summary-row">
|
||
<span>Mentés időtartam:</span>
|
||
<span class="schedule-summary-value">{{fmtDuration .Backup.LastBackup.Duration}}</span>
|
||
</div>
|
||
{{else}}
|
||
<div class="schedule-summary-row">
|
||
<span>Utolsó sikeres mentés:</span>
|
||
<span class="schedule-summary-value relative-time">Még nem futott</span>
|
||
</div>
|
||
{{end}}
|
||
<div class="schedule-summary-row">
|
||
<span>Megőrzés:</span>
|
||
<span class="schedule-summary-value">{{.Backup.Retention.KeepDaily}} napi · {{.Backup.Retention.KeepWeekly}} heti · {{.Backup.Retention.KeepMonthly}} havi</span>
|
||
</div>
|
||
</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}}
|
||
<div class="backup-table-empty">Nem található adatbázis mentés.</div>
|
||
{{end}}
|
||
</div>
|
||
|
||
<!-- Section 4: App data backup status (read-only) -->
|
||
{{if .Backup.AppDataInfo}}
|
||
<div class="backup-section-card">
|
||
<h3>Alkalmazás adatok</h3>
|
||
<p class="backup-section-desc">Az alkalmazások felhasználói adatainak mentési állapota. Beállítás az alkalmazás oldalán.</p>
|
||
<div class="app-backup-list">
|
||
{{range .Backup.AppDataInfo}}
|
||
<div class="app-backup-item">
|
||
<div class="app-backup-header">
|
||
<a href="/stacks/{{.StackName}}/deploy" class="app-backup-name-link">{{.DisplayName}}</a>
|
||
<div class="app-backup-status-row">
|
||
{{if .HasHDDData}}
|
||
{{if .StorageLabel}}<span class="meta-badge meta-badge-storage">{{.StorageLabel}}</span>{{end}}
|
||
{{if .BackupEnabled}}
|
||
<span class="app-backup-size mono">{{.HDDSizeHuman}}</span>
|
||
<span class="app-backup-status app-backup-active">Aktív</span>
|
||
{{else}}
|
||
<span class="app-backup-status app-backup-inactive">Inaktív</span>
|
||
{{end}}
|
||
{{else}}
|
||
<span class="app-backup-status app-backup-na">N/A</span>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
|
||
<!-- Section 4b: Cross-drive backups -->
|
||
{{if or .Backup.CrossDriveSummary .Backup.UnconfiguredApps}}
|
||
<div class="backup-section-card">
|
||
<h3>Másolatok másik meghajtóra</h3>
|
||
<p class="backup-section-desc">Alkalmazás adatok biztonsági másolata külső meghajtóra (3-2-1 szabály).</p>
|
||
|
||
{{if .Backup.CrossDriveWarnings}}
|
||
<div style="margin-bottom:1rem">
|
||
{{range .Backup.CrossDriveWarnings}}
|
||
<div class="alert alert-warning" style="margin-bottom:.5rem">{{.}}</div>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{if .Backup.CrossDriveSummary}}
|
||
<div class="cross-drive-list" style="margin-bottom:1rem">
|
||
{{range .Backup.CrossDriveSummary}}
|
||
<div class="cross-drive-item">
|
||
<div class="cross-drive-header">
|
||
<a href="/stacks/{{.StackName}}/deploy" class="cross-drive-name">{{.DisplayName}}</a>
|
||
<div class="cross-drive-meta">
|
||
<span class="meta-badge">{{.MethodLabel}}</span>
|
||
{{if .DestLabel}}<span class="meta-badge meta-badge-storage">→ {{.DestLabel}}</span>
|
||
{{else if .DestPath}}<span class="meta-badge meta-badge-storage">→ {{.DestPath}}</span>{{end}}
|
||
{{if eq .LastStatus "ok"}}<span class="meta-badge meta-badge-ok">{{.LastRunShort}}</span>
|
||
{{else if eq .LastStatus "error"}}<span class="meta-badge meta-badge-fail">Hiba</span>
|
||
{{else if eq .LastStatus "running"}}<span class="meta-badge">Fut...</span>
|
||
{{else}}<span class="meta-badge" style="color:var(--text-muted)">{{.ScheduleLabel}}</span>{{end}}
|
||
{{if .SizeHuman}}<span class="mono" style="font-size:.8rem;color:var(--text-muted)">{{.SizeHuman}}</span>{{end}}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{if .Backup.UnconfiguredApps}}
|
||
<div style="font-size:.85rem;color:var(--yellow);margin-bottom:1rem">
|
||
{{len .Backup.UnconfiguredApps}} alkalmazáshoz nincs beállítva:
|
||
{{range .Backup.UnconfiguredApps}}
|
||
<a href="/stacks/{{.StackName}}/deploy" style="color:var(--accent-blue)">{{.DisplayName}}</a>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
|
||
<div class="cross-drive-actions">
|
||
<button class="btn btn-sm btn-primary" onclick="triggerAllCrossDrive(this)">Összes futtatása most</button>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
|
||
<!-- Section 5: Snapshots -->
|
||
<div class="backup-section-card">
|
||
<h3>Pillanatképek</h3>
|
||
{{if .Backup.SnapshotHistory}}
|
||
<div class="backup-table-wrap">
|
||
<table class="snapshot-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Azonosító</th>
|
||
<th>Időpont</th>
|
||
<th>Méret</th>
|
||
<th>Új fájl</th>
|
||
<th>Változott</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{{range .Backup.SnapshotHistory}}
|
||
<tr>
|
||
<td class="mono">{{shortID .SnapshotID}}</td>
|
||
<td class="mono">{{fmtTime .Time}}</td>
|
||
<td class="mono">{{if .HasStats}}+{{.DataAdded}}{{else}}–{{end}}</td>
|
||
<td class="mono">{{if .HasStats}}{{.FilesNew}}{{else}}–{{end}}</td>
|
||
<td class="mono">{{if .HasStats}}{{.FilesChanged}}{{else}}–{{end}}</td>
|
||
</tr>
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="snapshot-footer">
|
||
Összesen: {{len .Backup.SnapshotHistory}} pillanatkép
|
||
{{if .Backup.RepoStats}} · {{.Backup.RepoStats.TotalSize}}{{end}}
|
||
</div>
|
||
{{else}}
|
||
<div class="backup-table-empty">Még nincs pillanatkép.</div>
|
||
{{end}}
|
||
</div>
|
||
|
||
<!-- Section 6: Repository -->
|
||
<div class="repo-card">
|
||
<h3>Tároló</h3>
|
||
<div class="repo-info-rows">
|
||
<div class="repo-info-row">
|
||
<span class="repo-label">Helyszín:</span>
|
||
<span class="repo-value mono">{{.Backup.RepoPath}} (helyi)</span>
|
||
</div>
|
||
{{if .Backup.RepoStats}}
|
||
<div class="repo-info-row">
|
||
<span class="repo-label">Méret:</span>
|
||
<span class="repo-value">{{.Backup.RepoStats.TotalSize}}</span>
|
||
</div>
|
||
<div class="repo-info-row">
|
||
<span class="repo-label">Pillanatképek:</span>
|
||
<span class="repo-value">{{.Backup.RepoStats.SnapshotCount}}</span>
|
||
</div>
|
||
{{end}}
|
||
<div class="repo-info-row">
|
||
<span class="repo-label">Integritás:</span>
|
||
<span class="repo-value">
|
||
{{if .Backup.LastCheckTime.IsZero}}
|
||
<span class="relative-time">Még nem ellenőrzött</span>
|
||
{{else if .Backup.LastCheckOK}}
|
||
<span class="backup-status-ok">Rendben</span> <span class="relative-time">({{fmtTime .Backup.LastCheckTime}})</span>
|
||
{{else}}
|
||
<span class="backup-status-fail">Hiba</span> <span class="relative-time">({{fmtTime .Backup.LastCheckTime}})</span>
|
||
{{end}}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Encryption key -->
|
||
{{if $.ResticPassword}}
|
||
<div class="repo-encryption">
|
||
<span class="repo-label">Titkosítási kulcs:</span>
|
||
<div class="repo-encryption-row">
|
||
<input type="password" id="restic-pw" class="restic-pw-field mono" value="{{$.ResticPassword}}" readonly>
|
||
<button type="button" class="btn btn-sm" onclick="toggleResticPw()">Megjelenítés</button>
|
||
<button type="button" class="btn btn-sm" onclick="copyResticPw()">Másolás</button>
|
||
</div>
|
||
<div class="repo-encryption-warn">
|
||
Mentse el biztonságos helyre! A kulcs nélkül a biztonsági mentések NEM állíthatók vissza.
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
|
||
<div class="repo-paths">
|
||
<span class="repo-label">Mentett útvonalak:</span>
|
||
<ul class="repo-path-list">
|
||
{{range .Backup.BackupPaths}}
|
||
<li class="mono">{{.}}</li>
|
||
{{end}}
|
||
</ul>
|
||
</div>
|
||
<div class="repo-remote">
|
||
<span class="repo-label">Távoli másolat:</span>
|
||
<div class="repo-remote-status">
|
||
<span class="relative-time">Nincs beállítva</span>
|
||
<span class="relative-time">(B2/S3/SFTP támogatás hamarosan)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Section 7: Restore -->
|
||
{{if .Backup.AppDataInfo}}
|
||
<div class="backup-section-card">
|
||
<h3>Visszaállítás</h3>
|
||
<div class="restore-section">
|
||
<div class="restore-form-row">
|
||
<label class="restore-label">Alkalmazás:</label>
|
||
<select id="restore-app" class="restore-select" onchange="onRestoreAppChange()">
|
||
<option value="">— Válasszon —</option>
|
||
{{range .Backup.AppDataInfo}}
|
||
{{if and .HasHDDData .BackupEnabled}}
|
||
<option value="{{.StackName}}" data-paths="{{range $i, $p := .HDDPaths}}{{if $i}},{{end}}{{$p.HostPath}}{{end}}">{{.DisplayName}}</option>
|
||
{{end}}
|
||
{{end}}
|
||
</select>
|
||
</div>
|
||
<div class="restore-form-row">
|
||
<label class="restore-label">Pillanatkép:</label>
|
||
<select id="restore-snapshot" class="restore-select">
|
||
<option value="">— Betöltés... —</option>
|
||
</select>
|
||
</div>
|
||
<div id="restore-paths" class="restore-paths" style="display:none;">
|
||
<span class="restore-label">Visszaállítandó útvonalak:</span>
|
||
<ul id="restore-paths-list" class="repo-path-list"></ul>
|
||
</div>
|
||
<div class="restore-warning">
|
||
<strong>FIGYELMEZTETÉS</strong><br>
|
||
A visszaállítás FELÜLÍRJA a kiválasztott alkalmazás jelenlegi adatait a mentés pillanatának állapotával.
|
||
Ez a művelet NEM vonható vissza!<br>
|
||
Javasoljuk az alkalmazás leállítását a visszaállítás előtt.
|
||
</div>
|
||
<div class="restore-confirm">
|
||
<label>
|
||
<input type="checkbox" id="restore-confirm-cb" onchange="onRestoreConfirmChange()">
|
||
Megértettem, visszaállítás saját felelősségre.
|
||
</label>
|
||
</div>
|
||
<div class="restore-actions">
|
||
<button type="button" class="btn btn-sm btn-danger" id="restore-btn" disabled onclick="submitRestore()">Visszaállítás indítása</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
|
||
{{end}}
|
||
|
||
<script>
|
||
function triggerAllCrossDrive(btn) {
|
||
btn.disabled = true;
|
||
btn.textContent = 'Indítás...';
|
||
fetch('/api/backup/cross-drive/run-all', {method: 'POST'})
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(d) {
|
||
if (!d.ok) {
|
||
alert('Hiba: ' + (d.error || 'Ismeretlen hiba'));
|
||
btn.disabled = false;
|
||
btn.textContent = 'Összes futtatása most';
|
||
return;
|
||
}
|
||
btn.textContent = 'Mentések futnak...';
|
||
setTimeout(function() { location.reload(); }, 5000);
|
||
})
|
||
.catch(function(e) {
|
||
alert('Hálózati hiba: ' + e.message);
|
||
btn.disabled = false;
|
||
btn.textContent = 'Összes futtatása most';
|
||
});
|
||
}
|
||
|
||
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' })
|
||
.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);
|
||
}
|
||
|
||
// Restic password toggle/copy
|
||
function toggleResticPw() {
|
||
var el = document.getElementById('restic-pw');
|
||
el.type = el.type === 'password' ? 'text' : 'password';
|
||
}
|
||
function copyResticPw() {
|
||
var el = document.getElementById('restic-pw');
|
||
navigator.clipboard.writeText(el.value).then(function() {
|
||
var btn = event.target;
|
||
btn.textContent = 'Másolva!';
|
||
setTimeout(function() { btn.textContent = 'Másolás'; }, 2000);
|
||
});
|
||
}
|
||
|
||
// Restore section
|
||
function onRestoreAppChange() {
|
||
var sel = document.getElementById('restore-app');
|
||
var opt = sel.options[sel.selectedIndex];
|
||
var pathsDiv = document.getElementById('restore-paths');
|
||
var pathsList = document.getElementById('restore-paths-list');
|
||
|
||
if (!opt.value) {
|
||
pathsDiv.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
var paths = (opt.getAttribute('data-paths') || '').split(',').filter(Boolean);
|
||
pathsList.innerHTML = '';
|
||
paths.forEach(function(p) {
|
||
var li = document.createElement('li');
|
||
li.className = 'mono';
|
||
li.textContent = p;
|
||
pathsList.appendChild(li);
|
||
});
|
||
pathsDiv.style.display = 'block';
|
||
|
||
// Load snapshots
|
||
fetch('/api/backup/snapshots')
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(data) {
|
||
var snapSel = document.getElementById('restore-snapshot');
|
||
snapSel.innerHTML = '<option value="">— Válasszon —</option>';
|
||
if (data.ok && data.data) {
|
||
data.data.forEach(function(s) {
|
||
var o = document.createElement('option');
|
||
o.value = s.short_id;
|
||
var t = new Date(s.time);
|
||
o.textContent = s.short_id + ' — ' + t.toLocaleString('hu-HU');
|
||
snapSel.appendChild(o);
|
||
});
|
||
}
|
||
});
|
||
|
||
document.getElementById('restore-confirm-cb').checked = false;
|
||
document.getElementById('restore-btn').disabled = true;
|
||
}
|
||
|
||
function onRestoreConfirmChange() {
|
||
var cb = document.getElementById('restore-confirm-cb');
|
||
var app = document.getElementById('restore-app').value;
|
||
var snap = document.getElementById('restore-snapshot').value;
|
||
document.getElementById('restore-btn').disabled = !(cb.checked && app && snap);
|
||
}
|
||
|
||
function submitRestore() {
|
||
var app = document.getElementById('restore-app').value;
|
||
var snap = document.getElementById('restore-snapshot').value;
|
||
if (!app || !snap) return;
|
||
|
||
var btn = document.getElementById('restore-btn');
|
||
btn.disabled = true;
|
||
btn.textContent = 'Visszaállítás folyamatban...';
|
||
|
||
var form = document.createElement('form');
|
||
form.method = 'POST';
|
||
form.action = '/backup/restore';
|
||
|
||
var f1 = document.createElement('input');
|
||
f1.type = 'hidden'; f1.name = 'stack_name'; f1.value = app;
|
||
form.appendChild(f1);
|
||
|
||
var f2 = document.createElement('input');
|
||
f2.type = 'hidden'; f2.name = 'snapshot_id'; f2.value = snap;
|
||
form.appendChild(f2);
|
||
|
||
document.body.appendChild(form);
|
||
form.submit();
|
||
}
|
||
|
||
// Auto-poll if backup is already running on page load
|
||
{{if .Backup}}{{if .Backup.Running}}
|
||
startBackupPolling();
|
||
{{end}}{{end}}
|
||
|
||
// Wire up snapshot selection change for restore confirm
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var snapSel = document.getElementById('restore-snapshot');
|
||
if (snapSel) snapSel.addEventListener('change', onRestoreConfirmChange);
|
||
});
|
||
</script>
|
||
|
||
{{template "layout_end" .}}
|
||
{{end}}
|