package handlers import ( "database/sql" "net/http" "github.com/gin-gonic/gin" "switchboard-core/models" ) // ── Team-Scoped Workflow Wrappers ──────────────── // // Thin wrappers that enforce team ownership before delegating // to the existing WorkflowHandler methods. Follows the same // pattern as CreateTeamTask / ListTeamTasks. // requireTeamWorkflow loads the workflow by :id and verifies // its team_id matches :teamId. Returns false (and writes an // HTTP error) if the check fails; true if the caller may proceed. func (h *WorkflowHandler) requireTeamWorkflow(c *gin.Context) bool { teamID := c.Param("teamId") wfID := c.Param("id") w, err := h.stores.Workflows.GetByID(c.Request.Context(), wfID) if err != nil { if err == sql.ErrNoRows { c.JSON(http.StatusNotFound, gin.H{"error": "workflow not found"}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load workflow"}) } return false } if w.TeamID == nil || *w.TeamID != teamID { c.JSON(http.StatusForbidden, gin.H{"error": "workflow does not belong to this team"}) return false } return true } // ── Adopt Global Workflow ──────────────────── // AdoptTeamWorkflow claims a global (team_id=NULL) workflow for this team. // POST /api/v1/teams/:teamId/workflows/:id/adopt func (h *WorkflowHandler) AdoptTeamWorkflow(c *gin.Context) { teamID := c.Param("teamId") wfID := c.Param("id") w, err := h.stores.Workflows.GetByID(c.Request.Context(), wfID) if err != nil { if err == sql.ErrNoRows { c.JSON(http.StatusNotFound, gin.H{"error": "workflow not found"}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load workflow"}) } return } if w.TeamID != nil { c.JSON(http.StatusConflict, gin.H{"error": "workflow already belongs to a team"}) return } patch := models.WorkflowPatch{TeamID: &teamID} if err := h.stores.Workflows.Update(c.Request.Context(), wfID, patch); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to adopt workflow"}) return } // Return the updated workflow c.Set("id", wfID) h.Get(c) } // ListGlobalWorkflows returns unowned workflows available for adoption. // GET /api/v1/teams/:teamId/workflows/available func (h *WorkflowHandler) ListGlobalWorkflows(c *gin.Context) { result, err := h.stores.Workflows.ListGlobal(c.Request.Context()) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list workflows"}) return } if result == nil { result = []models.Workflow{} } c.JSON(http.StatusOK, gin.H{"data": result}) } // ── Workflow CRUD ─────────────────────────── // ListTeamWorkflows returns workflows owned by the team. // GET /api/v1/teams/:teamId/workflows func (h *WorkflowHandler) ListTeamWorkflows(c *gin.Context) { teamID := c.Param("teamId") result, err := h.stores.Workflows.ListForTeam(c.Request.Context(), teamID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list workflows"}) return } if result == nil { result = []models.Workflow{} } c.JSON(http.StatusOK, gin.H{"data": result}) } // CreateTeamWorkflow creates a workflow scoped to the team. // POST /api/v1/teams/:teamId/workflows func (h *WorkflowHandler) CreateTeamWorkflow(c *gin.Context) { teamID := c.Param("teamId") c.Set("force_team_id", teamID) h.Create(c) } // GetTeamWorkflow returns a team workflow with stages. // GET /api/v1/teams/:teamId/workflows/:id func (h *WorkflowHandler) GetTeamWorkflow(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.Get(c) } // UpdateTeamWorkflow patches a team workflow. // PATCH /api/v1/teams/:teamId/workflows/:id func (h *WorkflowHandler) UpdateTeamWorkflow(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.Update(c) } // DeleteTeamWorkflow deletes a team workflow. // DELETE /api/v1/teams/:teamId/workflows/:id func (h *WorkflowHandler) DeleteTeamWorkflow(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.Delete(c) } // ── Stage CRUD ────────────────────────────── // ListTeamWorkflowStages returns stages for a team workflow. // GET /api/v1/teams/:teamId/workflows/:id/stages func (h *WorkflowHandler) ListTeamWorkflowStages(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.ListStages(c) } // CreateTeamWorkflowStage adds a stage to a team workflow. // POST /api/v1/teams/:teamId/workflows/:id/stages func (h *WorkflowHandler) CreateTeamWorkflowStage(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.CreateStage(c) } // UpdateTeamWorkflowStage updates a stage on a team workflow. // PUT /api/v1/teams/:teamId/workflows/:id/stages/:sid func (h *WorkflowHandler) UpdateTeamWorkflowStage(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.UpdateStage(c) } // DeleteTeamWorkflowStage removes a stage from a team workflow. // DELETE /api/v1/teams/:teamId/workflows/:id/stages/:sid func (h *WorkflowHandler) DeleteTeamWorkflowStage(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.DeleteStage(c) } // ReorderTeamWorkflowStages reorders stages on a team workflow. // PATCH /api/v1/teams/:teamId/workflows/:id/stages/reorder func (h *WorkflowHandler) ReorderTeamWorkflowStages(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.ReorderStages(c) } // ── Publish / Version ─────────────────────── // PublishTeamWorkflow publishes a team workflow version. // POST /api/v1/teams/:teamId/workflows/:id/publish func (h *WorkflowHandler) PublishTeamWorkflow(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.Publish(c) } // GetTeamWorkflowVersion returns a version snapshot for a team workflow. // GET /api/v1/teams/:teamId/workflows/:id/versions/:version func (h *WorkflowHandler) GetTeamWorkflowVersion(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } h.GetVersion(c) } // CloneTeamWorkflow deep-copies a team workflow. // POST /api/v1/teams/:teamId/workflows/:id/clone func (h *WorkflowHandler) CloneTeamWorkflow(c *gin.Context) { if !h.requireTeamWorkflow(c) { return } c.Set("force_team_id", c.Param("teamId")) h.Clone(c) }