package webapp import "net/http" // handleApps lists the inventory rows the most recent refresh loop // persisted. Each row links to the detail view at /apps/:id, which // Phase 1 implements as a single-process summary (comm, cmdline, // cwd, ports, uptime). func (s *Server) handleApps(w http.ResponseWriter, r *http.Request) { writeNoStore(w) sess, _ := sessionFromContext(r.Context()) apps := s.inventory.Snapshot() data := appsPageData{ basePageData: s.newBasePage(r, "Discovered apps", sess), Apps: apps, } if err := s.templates.Execute(w, "apps.html", data); err != nil { s.deps.Logger.Printf("render apps: %v", err) http.Error(w, "template error", http.StatusInternalServerError) } } // handleAppDetail renders the detail page for a single inventory // row. Phase 1 has no grouping, so :id is the row index in the // snapshot (matching the table id column). func (s *Server) handleAppDetail(w http.ResponseWriter, r *http.Request) { writeNoStore(w) sess, _ := sessionFromContext(r.Context()) id := r.PathValue("id") apps := s.inventory.Snapshot() var found *DiscoveredApp for i := range apps { if idMatch(&apps[i], id, i) { found = &apps[i] break } } if found == nil { http.NotFound(w, r) return } data := appDetailPageData{ basePageData: s.newBasePage(r, "App: "+found.Name, sess), App: *found, } if err := s.templates.Execute(w, "app_detail.html", data); err != nil { s.deps.Logger.Printf("render app detail: %v", err) http.Error(w, "template error", http.StatusInternalServerError) } } // idMatch matches either by pid (when id is a positive integer) or // by name (otherwise). Keeps URLs short and avoids leaking pids to // browser history. func idMatch(a *DiscoveredApp, id string, idx int) bool { if a.Name == id { return true } if id == pidOrIndex(a, idx) { return true } return false } func pidOrIndex(a *DiscoveredApp, idx int) string { if a.PID > 0 { return itoa(a.PID) } return itoa(idx) } func itoa(n int) string { const digits = "0123456789" if n == 0 { return "0" } neg := n < 0 if neg { n = -n } var buf [20]byte i := len(buf) for n > 0 { i-- buf[i] = digits[n%10] n /= 10 } if neg { i-- buf[i] = '-' } return string(buf[i:]) } type appsPageData struct { basePageData Apps []DiscoveredApp } type appDetailPageData struct { basePageData App DiscoveredApp }