ui+server: P2-18d pending hosts dashboard panel + expiry sweeper
Dashboard handler loads ListPendingHosts(now); template renders a warn-bordered panel above the host table with hostname, OS/arch, fingerprint (selectable / copyable), source IP, age, expiry. Each row carries an inline accept form (repo URL/user/password) plus a Reject button. cmd/server adds a 60s ticker calling DeleteExpiredPendingHosts so 1h-stale rows drop off.
This commit is contained in:
@@ -65,6 +65,60 @@
|
||||
</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 "{{$ph.Hostname}}" (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">
|
||||
|
||||
Reference in New Issue
Block a user