package web import ( "time" "gitea.dooplex.hu/admin/felhom-hub/internal/monitor" "gitea.dooplex.hu/admin/felhom-hub/internal/store" ) // heldAppRow is one line of the „Held apps" list (D53, `08` §10) on the host page and the Hosts page. type heldAppRow struct { CustomerID string HostID string // the customer's host, for the link on the Hosts page; "" when unknown Stack string Reason string Since time.Time Why string // the box's own sentence WhoClears string Reminded bool Stale bool // the box has not repeated it for heldAppStale — it may be down } // heldAppStaleAfter mirrors monitor's heldAppStale: a box repeats app_held daily. const heldAppStaleAfter = 50 * time.Hour // heldAppRows reads the held-app mirror, one customer's or every one's ("" = all). A read failure is logged and // shown as an empty list — the page must still render. func (s *Server) heldAppRows(customerID string, hostOf map[string]string, now time.Time) []heldAppRow { held, err := s.store.ListHeldApps(customerID) if err != nil { s.logger.Printf("[WARN] held apps: list for %q: %v", customerID, err) return nil } return toHeldAppRows(held, hostOf, now) } // hostHeldAppRows is one host's list. A host with no customer has none — never the fleet's („" lists every one). func (s *Server) hostHeldAppRows(customerID string, now time.Time) []heldAppRow { if customerID == "" { return nil } return s.heldAppRows(customerID, nil, now) } func toHeldAppRows(held []store.HeldApp, hostOf map[string]string, now time.Time) []heldAppRow { out := make([]heldAppRow, 0, len(held)) for _, h := range held { since := h.Since if since.IsZero() { since = h.FirstSeen } out = append(out, heldAppRow{CustomerID: h.CustomerID, HostID: hostOf[h.CustomerID], Stack: h.Stack, Reason: h.Reason, Since: since, Why: h.Why, WhoClears: monitor.HeldAppWhoClears(h.Reason), Reminded: !h.RemindedAt.IsZero(), Stale: now.Sub(h.LastSeen) > heldAppStaleAfter}) } return out }