- 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
129 lines
4.7 KiB
HTML
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}}
|