Files
deploy-felhom-compose/controller/internal/web/templates/dashboard.html
T
admin 37ff296a0d v0.4.5: Add dedicated Backup page (Biztonsági mentés)
New /backups page with full backup system visibility:
- Status overview cards (local/remote backup, DB count, repo size)
- Schedule section with next-run times and retention policy
- Database table with type, size, validation (table count), status
- Snapshot history table with per-snapshot stats
- Repository info card with paths, integrity status, remote placeholder
- "Mentés most" button with auto-refresh polling
- Empty state when backup not configured

Backend: SnapshotRecord history (ring buffer), DumpValidation,
ListDumpFiles, ListSnapshots, GetFullStatus, restic check tracking.
Server accepts scheduler for next-run time calculation.

Sidebar nav updated with 3rd item, dashboard backup card title clickable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 07:43:24 +01:00

188 lines
8.0 KiB
HTML

{{define "dashboard"}}
{{template "layout_start" .}}
<div class="page-header">
<h2>Vezérlőpult</h2>
<span class="domain-badge">{{.Domain}}</span>
</div>
<div class="stats-grid">
<a href="/stacks?filter=running" class="stat-card stat-running">
<div class="stat-value">{{.RunningCount}}</div>
<div class="stat-label">Futó alkalmazás</div>
</a>
<a href="/stacks?filter=stopped" class="stat-card stat-stopped">
<div class="stat-value">{{.StoppedCount}}</div>
<div class="stat-label">Leállítva</div>
</a>
<a href="/stacks" class="stat-card stat-total">
<div class="stat-value">{{.TotalCount}}</div>
<div class="stat-label">Összes alkalmazás</div>
</a>
</div>
{{if .SystemInfo.TotalMemMB}}
<div class="system-info-card">
<div class="system-info-items">
<div class="system-info-item">
<div class="system-info-header">
<span class="system-info-label">Memória</span>
<span class="system-info-value">{{fmtMB .SystemInfo.UsedMemMB}} / {{fmtMB .SystemInfo.TotalMemMB}} ({{printf "%.0f" .SystemInfo.MemPercent}}%)</span>
</div>
<div class="system-bar">
<div class="system-bar-fill system-bar-{{usageColor .SystemInfo.MemPercent}}" style="width:{{printf "%.0f" .SystemInfo.MemPercent}}%"></div>
</div>
</div>
<div class="system-info-item">
<div class="system-info-header">
<span class="system-info-label">CPU</span>
<span class="system-info-value">{{printf "%.0f" .SystemInfo.CPUPercent}}%</span>
</div>
<div class="system-bar">
<div class="system-bar-fill system-bar-{{usageColor .SystemInfo.CPUPercent}}" style="width:{{printf "%.0f" .SystemInfo.CPUPercent}}%"></div>
</div>
<div class="system-load-avg">Load: {{fmtLoad .SystemInfo.LoadAvg1}} / {{fmtLoad .SystemInfo.LoadAvg5}} / {{fmtLoad .SystemInfo.LoadAvg15}}</div>
</div>
{{if .SystemInfo.TemperatureCelsius}}
<div class="system-info-item system-info-item-compact">
<div class="system-info-header">
<span class="system-info-label">Hőmérséklet</span>
<span class="system-info-value">
<span class="temp-dot temp-dot-{{tempColor .SystemInfo.TemperatureCelsius}}"></span>
{{fmtTemp .SystemInfo.TemperatureCelsius}}
</span>
</div>
</div>
{{end}}
</div>
<div class="system-info-items" style="margin-top: 1rem;">
<div class="system-info-item">
<div class="system-info-header">
<span class="system-info-label">SSD tárhely</span>
<span class="system-info-value">{{fmtGB .SystemInfo.DiskUsedGB}} / {{fmtGB .SystemInfo.DiskTotalGB}} ({{printf "%.0f" .SystemInfo.DiskPercent}}%)</span>
</div>
<div class="system-bar">
<div class="system-bar-fill system-bar-{{usageColor .SystemInfo.DiskPercent}}" style="width:{{printf "%.0f" .SystemInfo.DiskPercent}}%"></div>
</div>
</div>
{{if .SystemInfo.HDDConfigured}}
<div class="system-info-item">
<div class="system-info-header">
<span class="system-info-label">Külső HDD</span>
<span class="system-info-value">{{fmtGB .SystemInfo.HDDUsedGB}} / {{fmtGB .SystemInfo.HDDTotalGB}} ({{printf "%.0f" .SystemInfo.HDDPercent}}%)</span>
</div>
<div class="system-bar">
<div class="system-bar-fill system-bar-{{usageColor .SystemInfo.HDDPercent}}" style="width:{{printf "%.0f" .SystemInfo.HDDPercent}}%"></div>
</div>
</div>
{{end}}
</div>
</div>
{{end}}
{{if .BackupEnabled}}
<div class="backup-status-card">
<h3><a href="/backups" class="backup-card-link">Biztonsági mentés</a></h3>
{{if .BackupStatus}}
<div class="backup-info-row">
<span class="backup-label">Utolsó mentés:</span>
<span class="backup-value">
{{if .BackupStatus.Success}}
<span class="backup-status-ok">{{.BackupStatus.LastRun.Format "2006-01-02 15:04"}}</span>
{{else}}
<span class="backup-status-fail">Sikertelen</span>
{{end}}
</span>
</div>
{{else}}
<div class="backup-info-row">
<span class="backup-label">Utolsó mentés:</span>
<span class="backup-value backup-status-none">Még nem futott</span>
</div>
{{end}}
{{if .DBDumpStatus}}
<div class="backup-info-row">
<span class="backup-label">Adatbázisok:</span>
<span class="backup-value">{{len .DBDumpStatus.Results}} mentve</span>
</div>
{{end}}
{{if .BackupStatus}}{{if .BackupStatus.RepoStats}}
<div class="backup-info-row">
<span class="backup-label">Tároló méret:</span>
<span class="backup-value">{{.BackupStatus.RepoStats.TotalSize}} ({{.BackupStatus.RepoStats.SnapshotCount}} pillanatkép)</span>
</div>
{{end}}{{end}}
<div class="backup-actions" style="margin-top: .75rem;">
<button class="btn btn-sm btn-primary" onclick="triggerBackup()" id="backup-btn">
{{if .BackupRunning}}Mentés folyamatban...{{else}}Mentés most{{end}}
</button>
</div>
</div>
{{end}}
<h3>Alkalmazások állapota</h3>
<div class="stack-list">
{{range .Stacks}}
<div class="stack-card stack-state-{{stateColor .State}}"{{if not .Protected}} data-href="/apps/{{.Meta.Slug}}"{{end}}>
<div class="stack-info">
<img class="stack-logo" src="{{logoURL .Meta.Slug}}"
alt="{{.Meta.DisplayName}}" onerror="this.onerror=function(){this.style.display='none'};this.src='{{logoPNGURL .Meta.Slug}}'">
<div>
<strong class="stack-name">{{.Meta.DisplayName}}</strong>
{{if .Meta.Description}}<span class="stack-desc">{{.Meta.Description}}</span>{{end}}
</div>
</div>
<div class="stack-actions">
<span class="stack-state-label">{{stateLabel .State}}</span>
{{if .Orphaned}}<span class="badge badge-orphaned">Elavult</span>{{end}}
{{if .Protected}}
<span class="badge badge-protected">Védett</span>
{{else if not .Deployed}}
<a href="/stacks/{{.Name}}/deploy" class="btn btn-sm btn-primary" onclick="return checkBeforeDeploy(event, '{{.Name}}')">Telepítés</a>
{{else}}
{{if isOperational .State}}
<button class="btn btn-sm btn-warning" onclick="stackAction('{{.Name}}', 'restart')"></button>
<button class="btn btn-sm btn-danger" onclick="stackAction('{{.Name}}', 'stop')"></button>
{{else}}
<button class="btn btn-sm btn-success" onclick="stackAction('{{.Name}}', 'start')"></button>
{{end}}
<a href="/stacks/{{.Name}}/logs" class="btn btn-sm btn-outline">Napló</a>
{{if .Orphaned}}<button class="btn btn-sm btn-danger" onclick="deleteOrphanStack('{{.Name}}')">Törlés</button>{{end}}
{{end}}
</div>
</div>
{{else}}
<div class="empty-state">
<p>Nincs elérhető alkalmazás.</p>
</div>
{{end}}
</div>
<script>
function triggerBackup() {
const btn = document.getElementById('backup-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');
} else {
btn.textContent = data.error || 'Hiba';
btn.disabled = false;
}
})
.catch(() => {
btn.textContent = 'Hiba';
btn.disabled = false;
});
}
</script>
{{template "layout_end" .}}
{{end}}