package handler import ( "context" "net/http" "strconv" "time" "ci/internal/model" "ci/internal/service" "github.com/gin-gonic/gin" ) // DeployHandler handles deployment endpoints. type DeployHandler struct { Svc *service.ProjectService } // NewDeployHandler creates a new DeployHandler. func NewDeployHandler(svc *service.ProjectService) *DeployHandler { return &DeployHandler{Svc: svc} } // GetDeployConfig returns the deploy config for a project. func (h *DeployHandler) GetDeployConfig(c *gin.Context) { id, err := parseID(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project id"}) return } cfg, err := h.Svc.GetDeployConfig(id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if cfg == nil { c.JSON(http.StatusNotFound, gin.H{"error": "no deploy config"}) return } c.JSON(http.StatusOK, cfg) } // UpdateDeployConfig creates or updates the deploy config for a project. func (h *DeployHandler) UpdateDeployConfig(c *gin.Context) { id, err := parseID(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project id"}) return } var cfg model.DeployConfig if err := c.ShouldBindJSON(&cfg); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } cfg.ProjectID = id if err := h.Svc.UpdateDeployConfig(&cfg); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Re-fetch to return clean state (sensitive fields hidden by json:"-") result, _ := h.Svc.GetDeployConfig(id) if result == nil { c.JSON(http.StatusOK, gin.H{"message": "created"}) return } c.JSON(http.StatusOK, result) } // DeleteDeployConfig removes the deploy config for a project. func (h *DeployHandler) DeleteDeployConfig(c *gin.Context) { id, err := parseID(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project id"}) return } if err := h.Svc.DeleteDeployConfig(id); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "deploy config deleted"}) } // TriggerDeploy starts a deployment for the project. func (h *DeployHandler) TriggerDeploy(c *gin.Context) { id, err := parseID(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project id"}) return } // Deploy in background ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) defer cancel() go func() { h.Svc.TriggerDeploy(ctx, id, nil) }() c.JSON(http.StatusAccepted, gin.H{"message": "deployment started"}) } // GetDeployRecords lists deploy history for a project. func (h *DeployHandler) GetDeployRecords(c *gin.Context) { id, err := parseID(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project id"}) return } records, err := h.Svc.GetDeployHistory(id, 50) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if records == nil { records = []model.DeployRecord{} } c.JSON(http.StatusOK, records) } // GetDeployLog returns the full log for a specific deployment. func (h *DeployHandler) GetDeployLog(c *gin.Context) { didStr := c.Param("did") did, err := parseIDStr(didStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid deploy id"}) return } logContent, err := h.Svc.GetDeployLog(did) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.String(http.StatusOK, logContent) } // parseIDStr parses an ID string for non-param-based IDs (e.g., deploy record ID from URL path). func parseIDStr(s string) (uint, error) { id, err := strconv.ParseUint(s, 10, 64) if err != nil { return 0, err } return uint(id), nil }