package handlers // Extension connection handlers — team scope. // Methods on TeamHandler. import ( "net/http" "github.com/gin-gonic/gin" "switchboard-core/models" ) // ListTeamConnections returns connections scoped to a team. func (h *TeamHandler) ListTeamConnections(c *gin.Context) { teamID := getTeamID(c) conns, err := h.stores.Connections.ListForTeam(c.Request.Context(), teamID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list team connections"}) return } c.JSON(http.StatusOK, gin.H{"data": maskConnectionSecrets(conns)}) } // CreateTeamConnection creates a connection scoped to a team. func (h *TeamHandler) CreateTeamConnection(c *gin.Context) { teamID := getTeamID(c) var req struct { Type string `json:"type" binding:"required"` PackageID string `json:"package_id" binding:"required"` Name string `json:"name" binding:"required"` Config models.JSONMap `json:"config"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Use ConnectionHandler helpers for encryption ch := &ConnectionHandler{stores: h.stores, vault: h.vault} secretFields := ch.lookupSecretFields(c, req.PackageID, req.Type) encConfig := ch.encryptSecrets(req.Config, secretFields, models.ScopeTeam, teamID) conn := &models.ExtConnection{ Type: req.Type, PackageID: req.PackageID, Scope: models.ScopeTeam, OwnerID: teamID, Name: req.Name, Config: encConfig, IsActive: true, } if err := h.stores.Connections.Create(c.Request.Context(), conn); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create team connection"}) return } c.JSON(http.StatusCreated, gin.H{ "id": conn.ID, "type": conn.Type, "name": conn.Name, }) } // UpdateTeamConnection updates a team-scoped connection. func (h *TeamHandler) UpdateTeamConnection(c *gin.Context) { teamID := getTeamID(c) id := c.Param("id") existing, err := h.stores.Connections.GetByID(c.Request.Context(), id) if err != nil || existing.Scope != models.ScopeTeam || existing.OwnerID != teamID { c.JSON(http.StatusNotFound, gin.H{"error": "connection not found"}) return } var req struct { Name *string `json:"name,omitempty"` Config models.JSONMap `json:"config,omitempty"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } patch := models.ExtConnectionPatch{Name: req.Name} if req.Config != nil { ch := &ConnectionHandler{stores: h.stores, vault: h.vault} secretFields := ch.lookupSecretFields(c, existing.PackageID, existing.Type) patch.Config = ch.encryptSecrets(req.Config, secretFields, models.ScopeTeam, teamID) } if err := h.stores.Connections.Update(c.Request.Context(), id, patch); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update connection"}) return } c.JSON(http.StatusOK, gin.H{"message": "connection updated"}) } // DeleteTeamConnection deletes a team-scoped connection. func (h *TeamHandler) DeleteTeamConnection(c *gin.Context) { teamID := getTeamID(c) id := c.Param("id") n, err := h.stores.Connections.DeleteByIDAndScope(c.Request.Context(), id, models.ScopeTeam, teamID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete connection"}) return } if n == 0 { c.JSON(http.StatusNotFound, gin.H{"error": "connection not found"}) return } c.JSON(http.StatusOK, gin.H{"message": "connection deleted"}) }