diff --git a/cmd/server/main.go b/cmd/server/main.go index c97b39d..a083a6d 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -156,6 +156,10 @@ func run() error { // shouldn't, but the queue exists either way). pendingDrainTick := time.NewTicker(30 * time.Second) defer pendingDrainTick.Stop() + // Pending-hosts expiry sweeper: drops announce rows past their 1h + // ceiling so the dashboard panel doesn't accumulate stale entries. + pendingExpiryTick := time.NewTicker(60 * time.Second) + defer pendingExpiryTick.Stop() mt := maintenance.New(st) go func() { for { @@ -176,6 +180,10 @@ func run() error { } case <-pendingDrainTick.C: srv.DrainAllDue(ctx) + case <-pendingExpiryTick.C: + if n, err := st.DeleteExpiredPendingHosts(ctx, time.Now().UTC()); err == nil && n > 0 { + slog.Info("expired pending hosts swept", "n", n) + } case <-maintenanceTick.C: decisions, err := mt.Decide(ctx, time.Now().UTC()) if err != nil { diff --git a/internal/server/http/ui_handlers.go b/internal/server/http/ui_handlers.go index ad23e03..f5d9594 100644 --- a/internal/server/http/ui_handlers.go +++ b/internal/server/http/ui_handlers.go @@ -109,9 +109,10 @@ func (s *Server) version() string { // dashboardPage is the data the dashboard template renders against. type dashboardPage struct { - Hosts []dashboardHostRow - HostCount int - Summary store.FleetSummary + Hosts []dashboardHostRow + HostCount int + Summary store.FleetSummary + PendingHosts []store.PendingHost // announce-and-approve queue (P2-18d) } // dashboardHostRow carries a host plus the per-row Run-now decision @@ -220,12 +221,18 @@ func (s *Server) handleUIDashboard(w stdhttp.ResponseWriter, r *stdhttp.Request) rows = append(rows, row) } + pending, perr := s.deps.Store.ListPendingHosts(r.Context(), time.Now().UTC()) + if perr != nil { + slog.Warn("ui dashboard: list pending hosts", "err", perr) + } + view := s.baseView(u) view.OpenAlerts = summary.OpenAlerts view.Page = dashboardPage{ - Hosts: rows, - HostCount: len(hosts), - Summary: summary, + Hosts: rows, + HostCount: len(hosts), + Summary: summary, + PendingHosts: pending, } if err := s.deps.UI.Render(w, "dashboard", view); err != nil { slog.Error("ui: render dashboard", "err", err) diff --git a/web/templates/pages/dashboard.html b/web/templates/pages/dashboard.html index d936850..fdcd487 100644 --- a/web/templates/pages/dashboard.html +++ b/web/templates/pages/dashboard.html @@ -65,6 +65,60 @@ + {{/* ---------- Pending hosts (announce-and-approve queue) ---------- */}} + {{if gt (len $page.PendingHosts) 0}} +
+
+
+

Pending hosts

+
{{len $page.PendingHosts}} waiting for approval
+
+
+
+ {{range $i, $ph := $page.PendingHosts}} +
+
+
+
+ {{$ph.Hostname}} + {{$ph.OS}}/{{$ph.Arch}} + agent {{$ph.AgentVersion}} + restic {{$ph.ResticVersion}} +
+
+ {{$ph.Fingerprint}} +
+
+ from {{$ph.AnnouncedFromIP}} · {{relTime $ph.FirstSeenAt}} + · expires {{relTime $ph.ExpiresAt}} +
+
+
+ + + +
+ + +
+
+
+
+ {{end}} +
+
+ {{end}} + {{/* ---------- hosts table ---------- */}}