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 // ProtectsData (D64 = B): a restore or put-back hold — the confirm step warns before „Clear hold" (`07` §8). ProtectsData bool } // holdProtectsData: the reasons whose hold keeps an app off half-restored data (`07` §8): a failed restore (the // reason is "" or restore_failed on older rows), a mixed restore, a put-back waiting for its drive. func holdProtectsData(reason string) bool { switch reason { case "", "restore_failed", "restore_mixed", "putback_pending": return true } return false } // 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, ProtectsData: holdProtectsData(h.Reason)}) } return out }