Files
deploy-felhom-compose/controller/internal/web/templates/backups.html
T
admin 9e8b48a32b fix: backup validation display and debug logging
- Add 4-branch template guard to handle zero-value DumpValidation
  (shows "–" instead of misleading "Hiba" when validation never ran)
- Add debug logging to ValidateDump for all code paths
- Add cross-check re-validation in RefreshCache to heal stale
  lastDBDump validation state from disk

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 14:56:34 +01:00

314 lines
12 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"}}
{{template "layout_start" .}}
<div class="page-header">
<h2>Biztonsági mentés</h2>
<span class="domain-badge">{{.Domain}}</span>
</div>
{{if not .Backup}}
<div class="backup-empty-state">
<div class="backup-empty-icon">&#128737;</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 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">&#10003;</div>
<div class="stat-label">Helyi mentés aktív</div>
</div>
{{else}}
<div class="stat-card stat-stopped">
<div class="stat-value">&#10007;</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: 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 5: 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>
<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>
{{end}}
<script>
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);
}
// Auto-poll if backup is already running on page load
{{if .Backup}}{{if .Backup.Running}}
startBackupPolling();
{{end}}{{end}}
</script>
{{template "layout_end" .}}
{{end}}