package api import ( "errors" "net/http" "github.com/alexei/tinyforge/internal/store" "github.com/go-chi/chi/v5" ) // containerView decorates a stored Container row with the human-readable // names the global Containers table needs (workload name, app name). // Decorating server-side avoids N+1 fetches on the frontend. type containerView struct { store.Container WorkloadName string `json:"workload_name"` AppID string `json:"app_id,omitempty"` AppName string `json:"app_name,omitempty"` } // listAllContainers handles GET /api/containers. // Query params: workload_id, kind, state, app_id (all optional, AND-combined). // Returns the global container index, newest first, decorated with workload // and app names. func (s *Server) listAllContainers(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() filter := store.ContainerFilter{ WorkloadID: q.Get("workload_id"), WorkloadKind: q.Get("kind"), State: q.Get("state"), AppID: q.Get("app_id"), } rows, err := s.store.ListContainers(filter) if err != nil { respondError(w, http.StatusInternalServerError, "list containers") return } // Pre-load workloads + apps so the join is in-memory rather than per-row. workloads, err := s.store.ListWorkloads("") if err != nil { respondError(w, http.StatusInternalServerError, "list workloads") return } wlByID := make(map[string]store.Workload, len(workloads)) for _, wl := range workloads { wlByID[wl.ID] = wl } apps, err := s.store.ListApps() if err != nil { respondError(w, http.StatusInternalServerError, "list apps") return } appByID := make(map[string]store.App, len(apps)) for _, a := range apps { appByID[a.ID] = a } out := make([]containerView, 0, len(rows)) for _, c := range rows { v := containerView{Container: c} if wl, ok := wlByID[c.WorkloadID]; ok { v.WorkloadName = wl.Name if wl.AppID != "" { v.AppID = wl.AppID if app, ok := appByID[wl.AppID]; ok { v.AppName = app.Name } } } out = append(out, v) } respondJSON(w, http.StatusOK, out) } // getContainer handles GET /api/containers/{id}. func (s *Server) getContainer(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") c, err := s.store.GetContainerByID(id) if err != nil { if errors.Is(err, store.ErrNotFound) { respondNotFound(w, "container") return } respondError(w, http.StatusInternalServerError, "get container") return } respondJSON(w, http.StatusOK, c) }