package api import ( "net/http" "strconv" "strings" "github.com/ez-api/ez-api/internal/dto" "github.com/ez-api/ez-api/internal/model" groupx "github.com/ez-api/foundation/group" "github.com/gin-gonic/gin" ) // CreateBinding godoc // @Summary Create a new binding // @Description Create a new (namespace, public_model) binding to a route group and selector // @Tags admin // @Accept json // @Produce json // @Security AdminAuth // @Param binding body dto.BindingDTO true "Binding Info" // @Success 201 {object} model.Binding // @Failure 400 {object} gin.H // @Failure 500 {object} gin.H // @Router /admin/bindings [post] func (h *Handler) CreateBinding(c *gin.Context) { var req dto.BindingDTO if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } ns := strings.TrimSpace(req.Namespace) pm := strings.TrimSpace(req.PublicModel) if ns == "" || pm == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "namespace and public_model required"}) return } rg := groupx.Normalize(req.RouteGroup) if strings.TrimSpace(rg) == "" { rg = "default" } st := strings.TrimSpace(req.Status) if st == "" { st = "active" } selectorType := strings.TrimSpace(req.SelectorType) if selectorType == "" { selectorType = "exact" } b := model.Binding{ Namespace: ns, PublicModel: pm, RouteGroup: rg, SelectorType: selectorType, SelectorValue: strings.TrimSpace(req.SelectorValue), Status: st, } if err := h.db.Create(&b).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create binding", "details": err.Error()}) return } if err := h.sync.SyncBindings(h.db); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()}) return } c.JSON(http.StatusCreated, b) } // ListBindings godoc // @Summary List bindings // @Description List all configured bindings // @Tags admin // @Produce json // @Security AdminAuth // @Success 200 {array} model.Binding // @Failure 500 {object} gin.H // @Router /admin/bindings [get] func (h *Handler) ListBindings(c *gin.Context) { var out []model.Binding if err := h.db.Find(&out).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list bindings", "details": err.Error()}) return } c.JSON(http.StatusOK, out) } // UpdateBinding godoc // @Summary Update a binding // @Description Update an existing binding // @Tags admin // @Accept json // @Produce json // @Security AdminAuth // @Param id path int true "Binding ID" // @Param binding body dto.BindingDTO true "Binding Info" // @Success 200 {object} model.Binding // @Failure 400 {object} gin.H // @Failure 404 {object} gin.H // @Failure 500 {object} gin.H // @Router /admin/bindings/{id} [put] func (h *Handler) UpdateBinding(c *gin.Context) { idParam := c.Param("id") id, err := strconv.Atoi(idParam) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) return } var existing model.Binding if err := h.db.First(&existing, id).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "binding not found"}) return } var req dto.BindingDTO if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if ns := strings.TrimSpace(req.Namespace); ns != "" { existing.Namespace = ns } if pm := strings.TrimSpace(req.PublicModel); pm != "" { existing.PublicModel = pm } if rg := strings.TrimSpace(req.RouteGroup); rg != "" { existing.RouteGroup = groupx.Normalize(rg) } if st := strings.TrimSpace(req.Status); st != "" { existing.Status = st } if t := strings.TrimSpace(req.SelectorType); t != "" { existing.SelectorType = t } if req.SelectorValue != "" { existing.SelectorValue = strings.TrimSpace(req.SelectorValue) } if err := h.db.Save(&existing).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update binding", "details": err.Error()}) return } if err := h.sync.SyncBindings(h.db); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()}) return } c.JSON(http.StatusOK, existing) }