package handler import ( "net/http" "strconv" "ci/internal/model" "ci/internal/service" "github.com/gin-gonic/gin" ) // ProjectHandler handles project CRUD endpoints. type ProjectHandler struct { Svc *service.ProjectService } func NewProjectHandler(svc *service.ProjectService) *ProjectHandler { return &ProjectHandler{Svc: svc} } // ListProjects returns all projects. func (h *ProjectHandler) ListProjects(c *gin.Context) { projects, err := h.Svc.ListProjects() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if projects == nil { projects = []model.Project{} } c.JSON(http.StatusOK, projects) } // GetProject returns a single project by ID. func (h *ProjectHandler) GetProject(c *gin.Context) { id, err := parseID(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project id"}) return } project, err := h.Svc.GetProject(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "project not found"}) return } c.JSON(http.StatusOK, project) } // CreateProject creates a new project. func (h *ProjectHandler) CreateProject(c *gin.Context) { var p model.Project if err := c.ShouldBindJSON(&p); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if p.Name == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"}) return } if p.Mode != "git" && p.Mode != "upload" { p.Mode = "git" // default } if err := h.Svc.CreateProject(&p); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, p) } // UpdateProject updates an existing project. func (h *ProjectHandler) UpdateProject(c *gin.Context) { id, err := parseID(c) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project id"}) return } existing, err := h.Svc.GetProject(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "project not found"}) return } var updates model.Project if err := c.ShouldBindJSON(&updates); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Preserve fields that should not be updated from the request updates.ID = existing.ID updates.CreatedAt = existing.CreatedAt updates.Status = existing.Status updates.PID = existing.PID // Preserve password if not sent if updates.GitPassword == "" { updates.GitPassword = existing.GitPassword } if err := h.Svc.UpdateProject(&updates); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, updates) } // DeleteProject deletes a project. func (h *ProjectHandler) DeleteProject(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.DeleteProject(id); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "deleted"}) } func parseID(c *gin.Context) (uint, error) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { return 0, err } return uint(id), nil }