Files
owenandClaude Sonnet 5 affdbfaa41 Restyle admin UI to dark theme, fix Docker /data volume permissions
- Rework the admin UI (topbar, sidebar, cards, tables, badges, stat
  tiles) into a dark theme with a light/dark toggle, matching the
  visual style requested for the project.
- Fix Dockerfile: chown /data to the distroless nonroot uid/gid before
  the VOLUME declaration so a freshly created named volume is seeded
  with writable permissions instead of root:root (was causing
  "unable to open database file" on first run).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QTXFrNP1imCW7jtfL5SZrw
2026-08-17 10:37:51 -05:00

129 lines
4.7 KiB
HTML

{{define "dashboard"}}{{template "header" .}}
{{$d := .Data}}
<div class="row g-3 mb-4">
<div class="col-sm-6 col-lg-3">
<div class="card stat-card h-100"><div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="stat-label">Active Proxy Hosts</div>
<i class="bi bi-globe2 stat-icon"></i>
</div>
<div class="stat-value">{{$d.ActiveProxyHosts}}</div>
</div></div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card stat-card h-100"><div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="stat-label">TCP / UDP Streams</div>
<i class="bi bi-arrow-left-right stat-icon"></i>
</div>
<div class="stat-value">{{$d.ActiveTCPStreams}} / {{$d.ActiveUDPStreams}}</div>
</div></div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card stat-card h-100"><div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="stat-label">Requests / sec</div>
<i class="bi bi-speedometer2 stat-icon"></i>
</div>
<div class="stat-value">{{printf "%.2f" $d.RequestsPerSecond}}</div>
<div class="stat-sub">1h average</div>
</div></div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card stat-card h-100"><div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="stat-label">Requests (24h)</div>
<i class="bi bi-graph-up stat-icon"></i>
</div>
<div class="stat-value">{{$d.TotalRequestsToday}}</div>
</div></div>
</div>
</div>
<div class="row g-3">
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">Status Breakdown (24h)</div>
<div class="card-body">
{{if $d.StatusBreakdown}}
<table class="table table-sm mb-0">
<tbody>
{{range $d.StatusBreakdown}}
<tr><td class="{{statusClass .Status}}">{{.Status}}</td><td>{{.Count}}</td></tr>
{{end}}
</tbody>
</table>
{{else}}<p class="text-secondary mb-0">No requests logged yet.</p>{{end}}
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">Backend Health</div>
<div class="card-body">
{{if $d.BackendHealth}}
<table class="table table-sm mb-0">
<tbody>
{{range $d.BackendHealth}}
<tr>
<td>{{.ProxyHost}}</td><td>{{.Backend}}</td>
<td>{{if .Healthy}}<span class="badge text-bg-success">healthy</span>{{else}}<span class="badge text-bg-danger">unhealthy</span>{{end}}</td>
</tr>
{{end}}
</tbody>
</table>
{{else}}<p class="text-secondary mb-0">No backends configured.</p>{{end}}
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">Top Hosts (24h)</div>
<div class="card-body">
{{if $d.TopHosts}}
<table class="table table-sm mb-0"><tbody>
{{range $d.TopHosts}}<tr><td>{{.Host}}</td><td>{{.Count}}</td></tr>{{end}}
</tbody></table>
{{else}}<p class="text-secondary mb-0">No data yet.</p>{{end}}
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">Top Clients (24h)</div>
<div class="card-body">
{{if $d.TopClients}}
<table class="table table-sm mb-0"><tbody>
{{range $d.TopClients}}<tr><td>{{.Host}}</td><td>{{.Count}}</td></tr>{{end}}
</tbody></table>
{{else}}<p class="text-secondary mb-0">No data yet.</p>{{end}}
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">Certificates Expiring Soon (30d)</div>
<div class="card-body">
{{if $d.CertsExpiringSoon}}
<table class="table table-sm mb-0"><tbody>
{{range $d.CertsExpiringSoon}}<tr><td>{{.Name}}</td><td>{{fmtTime (deref .NotAfter)}}</td></tr>{{end}}
</tbody></table>
{{else}}<p class="text-secondary mb-0">Nothing expiring soon.</p>{{end}}
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header">Recent Errors (24h)</div>
<div class="card-body">
{{if $d.RecentErrors}}
<table class="table table-sm mb-0"><tbody>
{{range $d.RecentErrors}}<tr><td>{{fmtTime .Timestamp}}</td><td class="{{statusClass .Status}}">{{.Status}}</td><td>{{.Host}}{{.Path}}</td></tr>{{end}}
</tbody></table>
{{else}}<p class="text-secondary mb-0">No recent errors.</p>{{end}}
</div>
</div>
</div>
</div>
{{template "footer" .}}{{end}}