Files
restic-manager/web/templates/pages/dashboard.html
T

154 lines
8.5 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "title"}}Dashboard · restic-manager{{end}}
{{define "content"}}
<div class="max-w-[1280px] mx-auto px-8">
{{$page := .Page}}
{{template "crit_banner" .Page}}
{{if eq $page.HostCount 0}}
{{/* ---------- empty state ---------- */}}
<div class="pt-14 pb-24">
<div class="empty-state">
<h1 class="text-lg font-medium tracking-[-0.005em]">No hosts yet.</h1>
<p class="text-pretty text-ink-mid mt-3 mx-auto max-w-[520px] text-[13px] leading-[1.65]">
<span class="mono text-ink">restic-manager</span> tracks backups across a fleet —
but theres nothing to track until you enrol your first host. Mint a token
from <span class="mono text-ink">+ Add host</span>, paste the install command on
a Linux box, and the host will appear here within seconds.
</p>
<div class="mt-7 flex items-center justify-center gap-2">
<a href="/hosts/new" class="btn btn-primary btn-lg">+ Add your first host</a>
<a href="/" class="btn">Reload</a>
</div>
<div class="mt-8 text-[12px] text-ink-fade">
Prerequisite: <span class="mono text-ink-mute">restic</span> ≥ 0.16 already installed on the target host.
</div>
</div>
</div>
{{else}}
{{/* ---------- fleet summary ---------- */}}
<div class="grid grid-cols-12 gap-6 pt-7 pb-2">
<div class="col-span-3">
<div class="text-[11px] text-ink-fade uppercase tracking-[0.08em] mb-2">Fleet</div>
<div class="mono text-[28px] font-medium tracking-[-0.02em]">{{$page.Summary.TotalHosts}} <span class="text-ink-mute text-[13px] font-normal">hosts</span></div>
<div class="flex items-center gap-3 mt-2.5 text-xs">
<span class="flex items-center gap-1.5"><span class="dot dot-online"></span><span class="mono text-ink-mid">{{$page.Summary.HostsOnline}}</span><span class="text-ink-mute">online</span></span>
<span class="flex items-center gap-1.5"><span class="dot dot-degraded"></span><span class="mono text-ink-mid">{{$page.Summary.HostsDegraded}}</span><span class="text-ink-mute">degraded</span></span>
<span class="flex items-center gap-1.5"><span class="dot dot-offline"></span><span class="mono text-ink-mid">{{$page.Summary.HostsOffline}}</span><span class="text-ink-mute">offline</span></span>
</div>
</div>
<div class="col-span-3">
<div class="text-[11px] text-ink-fade uppercase tracking-[0.08em] mb-2">Backed up</div>
<div class="mono text-[28px] font-medium tracking-[-0.02em]">{{bytes $page.Summary.RepoBytesTotal}}</div>
<div class="text-xs text-ink-mute mt-2.5"><span class="mono text-ink-mid">{{comma $page.Summary.SnapshotsTotal}}</span> snapshots total</div>
</div>
<div class="col-span-3">
<div class="text-[11px] text-ink-fade uppercase tracking-[0.08em] mb-2">Last 24h</div>
<div class="mono text-[28px] font-medium tracking-[-0.02em]">{{$page.Summary.JobsLast24h}} <span class="text-ink-mute text-[13px] font-normal">jobs</span></div>
<div class="text-xs mt-2.5">
<span class="mono text-ok">{{$page.Summary.JobsLast24hSucceeded}}</span> <span class="text-ink-mute">succeeded</span>
{{if gt $page.Summary.JobsLast24hFailed 0}} · <span class="mono text-bad">{{$page.Summary.JobsLast24hFailed}}</span> <span class="text-ink-mute">failed</span>{{end}}
{{if gt $page.Summary.JobsLast24hCancelled 0}} · <span class="mono text-ink-mid">{{$page.Summary.JobsLast24hCancelled}}</span> <span class="text-ink-mute">cancelled</span>{{end}}
</div>
</div>
<div class="col-span-3">
<div class="text-[11px] text-ink-fade uppercase tracking-[0.08em] mb-2">Open alerts</div>
{{if eq $page.Summary.OpenAlerts 0}}
<div class="mono text-[28px] font-medium tracking-[-0.02em] text-ink-mid">0 <span class="text-ink-mute text-[13px] font-normal">unresolved</span></div>
<div class="text-xs text-ink-mute mt-2.5">all clear</div>
{{else}}
<div class="mono text-[28px] font-medium tracking-[-0.02em] text-bad">{{$page.Summary.OpenAlerts}} <span class="text-ink-mute text-[13px] font-normal">unresolved</span></div>
<div class="text-xs text-ink-mute mt-2.5"><a href="/alerts" class="underline underline-offset-4 decoration-line">review →</a></div>
{{end}}
</div>
</div>
{{/* ---------- Pending hosts (announce-and-approve queue) ---------- */}}
{{if gt (len $page.PendingHosts) 0}}
<div class="pt-6">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-3">
<h2 class="text-[13px] font-semibold tracking-[0.01em] text-warn">Pending hosts</h2>
<div class="text-xs text-ink-fade">{{len $page.PendingHosts}} waiting for approval</div>
</div>
</div>
<div class="panel rounded-[7px] overflow-hidden"
style="border-color: color-mix(in oklch, var(--warn), transparent 70%);">
{{range $i, $ph := $page.PendingHosts}}
<div class="p-4 {{if not (eq $i 0)}}hairline{{end}}">
<div class="flex items-start justify-between gap-4">
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<span class="mono text-ink font-medium">{{$ph.Hostname}}</span>
<span class="mono text-[11px] text-ink-fade">{{$ph.OS}}/{{$ph.Arch}}</span>
<span class="mono text-[11px] text-ink-fade">agent {{$ph.AgentVersion}}</span>
<span class="mono text-[11px] text-ink-fade">restic {{$ph.ResticVersion}}</span>
</div>
<div class="mt-2 mono text-[12px] text-ink-mid select-all break-all"
style="font-family: var(--font-mono); padding: 6px 8px; background: var(--panel-hi); border-radius: 4px;">
{{$ph.Fingerprint}}
</div>
<div class="text-[11px] text-ink-fade mt-2">
from {{$ph.AnnouncedFromIP}} · {{relTime $ph.FirstSeenAt}}
· expires {{relTime $ph.ExpiresAt}}
</div>
</div>
<form method="post" action="/api/pending-hosts/{{$ph.ID}}/accept"
class="flex flex-col gap-2 flex-none" style="width: 320px;"
onsubmit="return confirm('Accept host &quot;{{$ph.Hostname}}&quot; (fingerprint {{$ph.Fingerprint}})? Make sure this matches what the install script printed.');">
<input type="text" name="repo_url" required placeholder="rest:http://…"
class="input mono" style="height: 28px; padding: 0 8px; font-size: 12px;">
<input type="text" name="repo_username" placeholder="repo username (optional)"
class="input mono" style="height: 28px; padding: 0 8px; font-size: 12px;">
<input type="password" name="repo_password" required placeholder="repo password"
class="input mono" style="height: 28px; padding: 0 8px; font-size: 12px;">
<div class="flex gap-2">
<button type="submit" class="btn btn-primary flex-1">Accept</button>
<button type="button" class="btn btn-danger flex-1"
hx-post="/api/pending-hosts/{{$ph.ID}}/reject"
hx-confirm="Reject pending host '{{$ph.Hostname}}'?"
hx-on::after-request="window.location.reload()">Reject</button>
</div>
</form>
</div>
</div>
{{end}}
</div>
</div>
{{end}}
{{/* ---------- hosts table ---------- */}}
<div class="pt-6 pb-4">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-3">
<h2 class="text-[13px] font-semibold tracking-[0.01em]">Hosts</h2>
<div class="text-xs text-ink-fade">{{$page.HostCount}} of {{$page.HostCount}}</div>
</div>
</div>
<div class="panel rounded-[7px] overflow-hidden">
<div class="host-row head hairline">
<div></div>
<div>Host</div>
<div>OS · arch</div>
<div>Last backup</div>
<div class="text-right">Repo size</div>
<div class="text-right">Snapshots</div>
<div>Alerts</div>
<div>Tags</div>
<div></div>
</div>
{{range $page.Hosts}}{{template "host_row" .}}{{end}}
</div>
</div>
{{end}}
</div>
{{end}}