// Package handler contains HTTP handlers for the Control Center API. // Each handler is a method on a Handler struct that receives its // dependencies through dependency injection — now wired to PostgreSQL-backed // repository implementations instead of in-memory stores. package handler import ( "encoding/json" "log/slog" "net/http" "time" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/models" "code.cubecraftcreations.com/CubeCraft-Creations/Control-Center/go-backend/internal/repository" "github.com/go-chi/chi/v5" "github.com/go-playground/validator/v10" ) // Handler groups all route handlers and their dependencies. type Handler struct { Agents repository.AgentRepo Sessions repository.SessionRepo Tasks repository.TaskRepo Projects repository.ProjectRepo validate *validator.Validate } // NewHandler returns a fully wired Handler with repository backends. func NewHandler( ar repository.AgentRepo, sr repository.SessionRepo, tr repository.TaskRepo, pr repository.ProjectRepo, ) *Handler { v := validator.New() v.RegisterValidation("agentStatus", validateAgentStatus) return &Handler{ Agents: ar, Sessions: sr, Tasks: tr, Projects: pr, validate: v, } } // ─── Agent Handlers ──────────────────────────────────────────────────────────── // ListAgents handles GET /api/agents. func (h *Handler) ListAgents(w http.ResponseWriter, r *http.Request) { statusFilter := models.AgentStatus(r.URL.Query().Get("status")) allAgents, err := h.Agents.List(r.Context(), statusFilter) if err != nil { slog.Error("list agents failed", "error", err) writeJSON(w, http.StatusInternalServerError, models.ErrorResponse{Error: "failed to list agents"}) return } page, pageSize := parsePagination(r) start, end := paginateSlice(len(allAgents), page, pageSize) totalCount, _ := h.Agents.Count(r.Context()) writeJSON(w, http.StatusOK, models.PaginatedResponse{ Data: allAgents[start:end], TotalCount: totalCount, Page: page, PageSize: pageSize, HasMore: end < len(allAgents), }) } // GetAgent handles GET /api/agents/{id}. func (h *Handler) GetAgent(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") agent, err := h.Agents.Get(r.Context(), id) if err != nil { writeJSON(w, http.StatusNotFound, models.ErrorResponse{Error: "agent not found"}) return } writeJSON(w, http.StatusOK, agent) } // CreateAgent handles POST /api/agents. func (h *Handler) CreateAgent(w http.ResponseWriter, r *http.Request) { var req models.CreateAgentRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeJSON(w, http.StatusBadRequest, models.ErrorResponse{Error: "invalid request body"}) return } if err := h.validate.Struct(req); err != nil { writeJSON(w, http.StatusUnprocessableEntity, models.ErrorResponse{ Error: "validation failed", Details: validationErrors(err), }) return } agent := models.AgentCardData{ ID: req.ID, DisplayName: req.DisplayName, Role: req.Role, Status: req.Status, CurrentTask: req.CurrentTask, SessionKey: req.SessionKey, Channel: req.Channel, LastActivity: time.Now().UTC().Format(time.RFC3339), } if err := h.Agents.Create(r.Context(), agent); err != nil { writeJSON(w, http.StatusConflict, models.ErrorResponse{Error: "agent with this ID already exists"}) return } writeJSON(w, http.StatusCreated, agent) } // UpdateAgent handles PUT /api/agents/{id}. func (h *Handler) UpdateAgent(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") var req models.UpdateAgentRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeJSON(w, http.StatusBadRequest, models.ErrorResponse{Error: "invalid request body"}) return } if err := h.validate.Struct(req); err != nil { writeJSON(w, http.StatusUnprocessableEntity, models.ErrorResponse{ Error: "validation failed", Details: validationErrors(err), }) return } agent, err := h.Agents.Update(r.Context(), id, req) if err != nil { writeJSON(w, http.StatusNotFound, models.ErrorResponse{Error: "agent not found"}) return } writeJSON(w, http.StatusOK, agent) } // DeleteAgent handles DELETE /api/agents/{id}. func (h *Handler) DeleteAgent(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") if err := h.Agents.Delete(r.Context(), id); err != nil { writeJSON(w, http.StatusNotFound, models.ErrorResponse{Error: "agent not found"}) return } w.WriteHeader(http.StatusNoContent) } // AgentHistory handles GET /api/agents/{id}/history. func (h *Handler) AgentHistory(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") if _, err := h.Agents.Get(r.Context(), id); err != nil { writeJSON(w, http.StatusNotFound, models.ErrorResponse{Error: "agent not found"}) return } // History is not currently persisted in PostgreSQL — return stub. writeJSON(w, http.StatusOK, []models.AgentStatusHistoryEntry{}) }