Files
ez-api/internal/api/binding_handler.go
zenfun ed6446e586 feat(api): add admin endpoints for binding management
Implement handlers for creating, listing, and updating model bindings.
Register new routes in the admin server group and add DTO definitions.
Update provider handlers to trigger binding synchronization on changes
to ensure upstream mappings remain current.
2025-12-17 00:37:02 +08:00

161 lines
4.4 KiB
Go

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)
}