Files
deploy-felhom-compose/controller/internal/web/templates/dashboard.html
T
admin bdbe170a54 feat: storage watchdog — USB disconnect detection, auto-stop, safe eject, auto-reconnect (v0.17.0)
New storage watchdog monitors registered storage paths every 5s. On disconnect
(3 consecutive probe failures), auto-stops affected apps, lazy-unmounts stale
VFS entries, fires alerts/notifications/hub report. On reconnect (UUID detected),
auto-remounts via fstab, cleans stale restic locks, offers app restart.

Safe disconnect UI for USB drives: confirmation dialog, stop apps, sync, unmount.
Disconnected state visible across all pages (dashboard, settings, backups, monitoring)
with hatched red bars and badges. Backup guards skip disconnected drives.

22 files changed (1 new: monitor/watchdog.go), ~1500 lines added.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 19:42:26 +01:00

197 lines
8.7 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>
<span class="temp-value-pill temp-pill-{{tempColor .SystemInfo.TemperatureCelsius}}">{{fmtTemp .SystemInfo.TemperatureCelsius}}</span>
</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">Rendszer (/)</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>
{{range .StorageBars}}
{{if .Disconnected}}
<div class="system-info-item storage-disconnected">
<div class="system-info-header">
<span class="system-info-label">{{.Label}}</span>
<span class="system-info-value badge-error" style="font-size:.75rem">Leválasztva</span>
</div>
<div class="system-bar"><div class="system-bar-disconnected"></div></div>
</div>
{{else}}
<div class="system-info-item">
<div class="system-info-header">
<span class="system-info-label">{{.Label}}</span>
<span class="system-info-value">{{fmtGB .UsedGB}} / {{fmtGB .TotalGB}} ({{printf "%.0f" .Percent}}%)</span>
</div>
<div class="system-bar">
<div class="system-bar-fill system-bar-{{usageColor .Percent}}" style="width:{{printf "%.0f" .Percent}}%"></div>
</div>
</div>
{{end}}
{{end}}
</div>
{{if .DiskWarnings}}
<div class="inline-warnings">
{{range .DiskWarnings}}
<div class="inline-warning inline-warning-{{.Level}}">
<span class="inline-warning-dot"></span>
<span class="inline-warning-text">{{.Message}}</span>
{{if .Link}}<a href="{{.Link}}" class="inline-warning-link">{{.LinkText}} →</a>{{end}}
</div>
{{end}}
</div>
{{end}}
</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 gt .CrossDriveTotal 0}}
<div class="backup-info-row">
<span class="backup-label">2. mentés:</span>
<span class="backup-value">
{{if eq .CrossDriveConfigured 0}}
<span style="color:var(--yellow)">⚠ Nincs beállítva</span>
{{else}}
<span class="{{if gt .CrossDriveFailed 0}}backup-status-fail{{else}}backup-status-ok{{end}}">{{.CrossDriveConfigured}} / {{.CrossDriveTotal}} alk.</span>
{{end}}
</span>
</div>
{{end}}
{{if gt .CrossDriveFailed 0}}
<div class="backup-info-row">
<span class="backup-label" style="color:var(--red)">Figyelmeztetés:</span>
<span class="backup-value backup-status-fail">⚠ {{.CrossDriveFailed}} 2. mentés sikertelen</span>
</div>
{{end}}
</div>
{{end}}
<h3>Telepített alkalmazások</h3>
<div class="stack-list">
{{range .Stacks}}
<div class="stack-card stack-state-{{stateColor .State}}"{{if .Meta.Slug}} 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>
{{if isOperational .State}}
<button class="btn btn-sm btn-warning" onclick="stackAction(event, '{{.Name}}', 'restart')"></button>
{{end}}
{{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(event, '{{.Name}}', 'restart')"></button>
<button class="btn btn-sm btn-danger" onclick="stackAction(event, '{{.Name}}', 'stop')"></button>
{{else}}
<button class="btn btn-sm btn-success" onclick="stackAction(event, '{{.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>
{{template "layout_end" .}}
{{end}}