package handler import ( "net/http" "time" "github.com/gin-gonic/gin" "flowstack/internal/database" "flowstack/internal/model" ) // ListPipelines GET /api/pipelines func ListPipelines(c *gin.Context) { var list []model.Pipeline database.DB.Order("updated_at DESC").Find(&list) c.JSON(http.StatusOK, list) } // GetPipeline GET /api/pipelines/:id —— 详情含画布节点与连线 func GetPipeline(c *gin.Context) { var p model.Pipeline if err := database.DB.First(&p, c.Param("id")).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "pipeline not found"}) return } var nodes []model.PipelineNode database.DB.Where("pipeline_id = ?", p.ID).Order("position_x").Find(&nodes) var edges []model.PipelineEdge database.DB.Where("pipeline_id = ?", p.ID).Find(&edges) c.JSON(http.StatusOK, gin.H{ "pipeline": p, "nodes": nodes, "edges": edges, "name": p.Name, "description": p.Description, "type": p.Type, "sourceRepo": p.SourceRepo, "sourceBranch": p.SourceBranch, "gitopsRepo": p.GitopsRepo, "gitopsPath": p.GitopsPath, "deployType": p.DeployType, "env": p.Env, "id": p.ID, "createdAt": p.CreatedAt, "updatedAt": p.UpdatedAt, }) } // CreatePipeline POST /api/pipelines func CreatePipeline(c *gin.Context) { var input struct { Name string `json:"name" binding:"required"` Description string `json:"description"` Type string `json:"type"` SourceRepo string `json:"sourceRepo"` SourceBranch string `json:"sourceBranch"` DeployType string `json:"deployType"` Env string `json:"env"` } if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "name 不能为空"}) return } setDefault(&input.Type, "native") setDefault(&input.SourceBranch, "main") setDefault(&input.DeployType, "host") setDefault(&input.Env, "staging") p := model.Pipeline{ Name: input.Name, Description: input.Description, Type: input.Type, SourceRepo: input.SourceRepo, SourceBranch: input.SourceBranch, DeployType: input.DeployType, Env: input.Env, } if err := database.DB.Create(&p).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, p) } // UpdatePipeline PUT /api/pipelines/:id func UpdatePipeline(c *gin.Context) { var p model.Pipeline if err := database.DB.First(&p, c.Param("id")).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "pipeline not found"}) return } var input map[string]any if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } allowed := map[string]bool{"name": true, "description": true, "sourceRepo": true, "sourceBranch": true, "gitopsRepo": true, "gitopsPath": true, "deployType": true, "env": true} updates := map[string]any{} for k, v := range input { if allowed[k] { updates[k] = v } } if len(updates) > 0 { updates["updated_at"] = time.Now() database.DB.Model(&p).Updates(updates) } c.JSON(http.StatusOK, p) } // DeletePipeline DELETE /api/pipelines/:id —— 级联删除节点与连线 func DeletePipeline(c *gin.Context) { id := c.Param("id") database.DB.Where("pipeline_id = ?", id).Delete(&model.PipelineEdge{}) database.DB.Where("pipeline_id = ?", id).Delete(&model.PipelineNode{}) database.DB.Delete(&model.Pipeline{}, id) c.JSON(http.StatusOK, gin.H{"success": true}) } // SaveNodes PUT /api/pipelines/:id/nodes —— 全量覆盖保存 func SaveNodes(c *gin.Context) { pipelineID := c.Param("id") var p model.Pipeline if err := database.DB.First(&p, pipelineID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "pipeline not found"}) return } var body struct { Nodes []model.PipelineNode `json:"nodes"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } database.DB.Where("pipeline_id = ?", p.ID).Delete(&model.PipelineNode{}) for i := range body.Nodes { body.Nodes[i].ID = 0 body.Nodes[i].PipelineID = p.ID database.DB.Create(&body.Nodes[i]) } database.DB.Model(&p).Update("updated_at", time.Now()) c.JSON(http.StatusOK, gin.H{"success": true}) } // SaveEdges PUT /api/pipelines/:id/edges —— 全量覆盖保存 func SaveEdges(c *gin.Context) { pipelineID := c.Param("id") var p model.Pipeline if err := database.DB.First(&p, pipelineID).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "pipeline not found"}) return } var body struct { Edges []model.PipelineEdge `json:"edges"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } database.DB.Where("pipeline_id = ?", p.ID).Delete(&model.PipelineEdge{}) for i := range body.Edges { body.Edges[i].ID = 0 body.Edges[i].PipelineID = p.ID database.DB.Create(&body.Edges[i]) } database.DB.Model(&p).Update("updated_at", time.Now()) c.JSON(http.StatusOK, gin.H{"success": true}) } func setDefault(v *string, def string) { if *v == "" { *v = def } }