v0.12.0 — Backup page overhaul: unified app rows, bug fixes, sequential chaining
Bug fixes: - GetFullStatus() returns deep copy; CrossDriveSummary/UnconfiguredApps/CrossDriveWarnings are always nil in the copy so the handler builds them fresh (fixes duplicate-apps bug) - Replace binary IsMountPoint check with tiered CheckBackupDestination() — path-not-exist, not-writable, system-drive (warning), disk >90-95% full; shown as warning vs critical - Remove dead settingsAppBackupHandler / POST /settings/app-backup route (toggle wrote to settings.json but nothing consumed the flag) Architecture: - Unified per-app backup rows: new AppBackupRow struct + buildAppBackupRows() replaces the two old sections with expandable rows showing all 3 layers per app - Sequential backup chaining: cross-drive runs immediately after restic (removed independent cross-drive-daily/cross-drive-weekly scheduler jobs) - Deploy page: remove "Csak kézi indítás" schedule option; add weekly consistency note Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -233,84 +233,109 @@
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<!-- Section 4: App data backup status (read-only) -->
|
||||
<!-- Section 4: Unified per-app backup status -->
|
||||
{{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>
|
||||
<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}}">
|
||||
<div class="app-backup-row-header" onclick="toggleBackupDetail(this)">
|
||||
<span class="status-dot status-{{.Status}}" title="{{.StatusText}}"></span>
|
||||
<span class="app-backup-row-name">{{.DisplayName}}</span>
|
||||
<div class="app-backup-row-meta">
|
||||
{{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}}
|
||||
<span class="meta-badge">Auto</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<span class="expand-icon">▶</span>
|
||||
</div>
|
||||
<div class="app-backup-row-detail" style="display:none">
|
||||
<div class="backup-layers">
|
||||
<!-- DB layer -->
|
||||
<div class="backup-layer-row">
|
||||
<span class="layer-label">Adatbázis mentés</span>
|
||||
{{if .HasDB}}
|
||||
<span class="layer-badge">Auto</span>
|
||||
{{if .DBLastRun}}
|
||||
<span class="layer-last">Utolsó: {{.DBLastRun}}
|
||||
{{if eq .DBLastStatus "ok"}}<span class="text-ok">✓</span>
|
||||
{{else if eq .DBLastStatus "error"}}<span class="text-error">✗</span>{{end}}
|
||||
</span>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<span class="app-backup-status app-backup-na">N/A</span>
|
||||
<span class="layer-na">— (nincs adatbázis)</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<!-- Volume layer -->
|
||||
<div class="backup-layer-row">
|
||||
<span class="layer-label">Docker kötetek</span>
|
||||
<span class="layer-badge">Auto</span>
|
||||
{{if .VolumeLastRun}}
|
||||
<span class="layer-last">Utolsó: {{.VolumeLastRun}}
|
||||
{{if eq .VolumeLastStatus "ok"}}<span class="text-ok">✓</span>
|
||||
{{else if eq .VolumeLastStatus "error"}}<span class="text-error">✗</span>{{end}}
|
||||
</span>
|
||||
{{end}}
|
||||
</div>
|
||||
<!-- User data layer -->
|
||||
<div class="backup-layer-row{{if not .HasHDDData}} layer-row-na{{end}}">
|
||||
<span class="layer-label">Felhasználói adatok</span>
|
||||
{{if .HasUserData}}
|
||||
{{if .UserDataConfigured}}
|
||||
<span class="layer-method">{{.UserDataMethod}}</span>
|
||||
<span class="layer-dest">→ {{.UserDataDest}}</span>
|
||||
<span class="layer-schedule">{{.UserDataSchedule}}</span>
|
||||
{{if .UserDataLastRun}}
|
||||
<span class="layer-last">Utolsó: {{.UserDataLastRun}}
|
||||
<span class="{{if eq .UserDataLastStatus "ok"}}text-ok{{else if eq .UserDataLastStatus "error"}}text-error{{else if eq .UserDataLastStatus "running"}}text-muted{{end}}">
|
||||
{{.UserDataStatusBadge}}
|
||||
</span>
|
||||
</span>
|
||||
{{end}}
|
||||
<div class="layer-actions">
|
||||
<a href="/stacks/{{.StackName}}/deploy" class="btn btn-xs btn-outline">Beállítás</a>
|
||||
<button class="btn btn-xs btn-outline"
|
||||
onclick="triggerCrossDriveBackup('{{.StackName}}', this)">
|
||||
Futtatás most</button>
|
||||
</div>
|
||||
{{else}}
|
||||
<span class="layer-unconfigured">⚠ Nincs beállítva</span>
|
||||
<a href="/stacks/{{.StackName}}/deploy" class="btn btn-xs">Beállítás →</a>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<span class="layer-na">— (nincs HDD adat)</span>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{if .Warnings}}
|
||||
<div class="layer-warnings">
|
||||
{{range .Warnings}}
|
||||
<div class="backup-layer-warning">{{.}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</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 class="cross-drive-actions" style="margin-top:1rem">
|
||||
<button class="btn btn-sm btn-outline" onclick="triggerAllCrossDrive(this)">Összes HDD mentés futtatása most</button>
|
||||
</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}}
|
||||
|
||||
@@ -463,6 +488,40 @@
|
||||
{{end}}
|
||||
|
||||
<script>
|
||||
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 triggerCrossDriveBackup(stackName, btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Fut...';
|
||||
fetch('/api/stacks/' + stackName + '/cross-backup/run', {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 = 'Futtatás most';
|
||||
return;
|
||||
}
|
||||
btn.textContent = 'Fut...';
|
||||
setTimeout(function() { location.reload(); }, 5000);
|
||||
})
|
||||
.catch(function(e) {
|
||||
alert('Hálózati hiba: ' + e.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Futtatás most';
|
||||
});
|
||||
}
|
||||
|
||||
function triggerAllCrossDrive(btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Indítás...';
|
||||
|
||||
Reference in New Issue
Block a user