package demos import ( "errors" "net/http" "github.com/gin-gonic/gin" "github.com/omnex/control-plane/api/internal/auth" ) type Handler struct { svc *Service helm *HelmProvisioner // interface déjà implémentée par HelmProvisioner } func NewHandler(svc *Service, helm *HelmProvisioner) *Handler { return &Handler{svc: svc, helm: helm} } type createRequest struct { LeadID string `json:"lead_id" binding:"omitempty,uuid4"` Username string `json:"username" binding:"omitempty,min=3,max=64,alphanum"` } type DetailDemoUserRequest struct { Username string `json:"username" binding:"required,min=3,max=64,alphanum"` } type DemoDetailsResponse struct { Username string `json:"username"` Namespace string `json:"namespace"` State ResourceState `json:"state"` } // Create : POST /demos — provisionne une démo (202 Accepted, worker asynchrone). func (h *Handler) Create(c *gin.Context) { var req createRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "requête invalide"}) return } d, err := h.svc.Create(req.LeadID, req.Username) if err != nil { if errors.Is(err, ErrCapacityReached) { c.JSON(http.StatusConflict, gin.H{"error": "capacité maximale de démos atteinte"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur serveur"}) return } c.JSON(http.StatusAccepted, d) } // List : GET /demos. func (h *Handler) List(c *gin.Context) { items, err := h.svc.List() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur serveur"}) return } c.JSON(http.StatusOK, gin.H{"items": items}) } func (h *Handler) ListDetails(c *gin.Context) { p := auth.PrincipalFrom(c) if p == nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "non authentifié"}) return } if p.Role != "admin" { c.JSON(http.StatusForbidden, gin.H{"error": "vous n'avez pas les droits"}) return } var req DetailDemoUserRequest if err := c.ShouldBindJSON(&req); err != nil || req.Username == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "username requis"}) return } demo, err := h.svc.store.GetDemoByUsername(req.Username) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "démo introuvable"}) return } state, err := h.helm.GetResourceState(c.Request.Context(), demo.Namespace) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "impossible de récupérer l'état des ressources"}) return } c.JSON(http.StatusOK, DemoDetailsResponse{ Username: req.Username, Namespace: demo.Namespace, State: state, }) } // Get : GET /demos/:id. func (h *Handler) Get(c *gin.Context) { d, err := h.svc.Get(c.Param("id")) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "démo introuvable"}) return } c.JSON(http.StatusOK, d) } // Delete : DELETE /demos/:id — teardown + libération du pool. func (h *Handler) Delete(c *gin.Context) { d, err := h.svc.Delete(c.Param("id")) if err != nil { if errors.Is(err, ErrNotFound) { c.JSON(http.StatusNotFound, gin.H{"error": "démo introuvable"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur serveur"}) return } c.JSON(http.StatusOK, d) } // Extend : POST /demos/:id/extend — prolonge de 30 jours. func (h *Handler) Extend(c *gin.Context) { d, err := h.svc.Extend(c.Param("id")) if err != nil { switch { case errors.Is(err, ErrNotFound): c.JSON(http.StatusNotFound, gin.H{"error": "démo introuvable"}) case errors.Is(err, ErrNotExtendable): c.JSON(http.StatusConflict, gin.H{"error": "démo non prolongeable"}) default: c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur serveur"}) } return } c.JSON(http.StatusOK, d) }