package webapp import ( "net/http" "strings" "time" ) // handleOverview is the landing page after login. Phase 1 shows the // worker status, recent results counts, and a tail of the worker // log buffer. The data shape will grow in later phases as the // heartbeat inventory and 24h result counts come online. func (s *Server) handleOverview(w http.ResponseWriter, r *http.Request) { writeNoStore(w) sess, _ := sessionFromContext(r.Context()) snap, snapAt := s.metrics.Last() data := overviewPageData{ basePageData: s.newBasePage(r, "Overview", sess), WorkerID: workerIDOrDash(s.deps.Runner), RegionCode: regionOrDash(s.deps.Runner), WorkerState: workerState(s.deps.Runner, s.deps.StartedAt), LastAckAt: lastAckOrZero(s.deps.Runner), StartedAt: s.deps.StartedAt, Snapshot: snap, SnapshotAt: snapAt, DiscoveredCount: len(s.inventory.Snapshot()), ResultCount: len(s.deps.Runner.RecentResults(1000)), NotifCount: len(s.deps.Runner.RecentNotifications(1000)), LogTail: s.logBuffer.Tail(20), } if err := s.templates.Execute(w, "overview.html", data); err != nil { s.deps.Logger.Printf("render overview: %v", err) http.Error(w, "template error", http.StatusInternalServerError) } } func workerIDOrDash(v WorkerView) string { if v == nil { return "—" } return v.WorkerID() } func regionOrDash(v WorkerView) string { if v == nil { return "—" } return v.RegionCode() } func workerState(v WorkerView, startedAt time.Time) string { if v == nil { return "—" } if last := v.LastHeartbeatAck(); !last.IsZero() && last.After(startedAt) { return "connected" } return "starting" } func lastAckOrZero(v WorkerView) time.Time { if v == nil { return time.Time{} } return v.LastHeartbeatAck() } // overviewPageData is the data backing overview.html. type overviewPageData struct { basePageData WorkerID string RegionCode string WorkerState string LastAckAt time.Time StartedAt time.Time Snapshot Snapshot SnapshotAt time.Time DiscoveredCount int ResultCount int NotifCount int LogTail []string } var _ = strings.TrimSpace