package handlers import ( "log" "net/http" "os/exec" "ezops/internal/models" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // ==================== 工具配置 CRUD ==================== // ToolConfigHandler handles deploy tool configuration type ToolConfigHandler struct { db *gorm.DB } // NewToolConfigHandler creates handler func NewToolConfigHandler(db *gorm.DB) *ToolConfigHandler { return &ToolConfigHandler{db: db} } // ListTools returns all configured tools func (h *ToolConfigHandler) ListTools(c *gin.Context) { var tools []models.DeployTool h.db.Order("sort_order ASC").Find(&tools) c.JSON(http.StatusOK, models.Response{Code: 200, Data: models.ListResponse{List: tools, Total: int64(len(tools))}}) } // GetTool returns a single tool func (h *ToolConfigHandler) GetTool(c *gin.Context) { var tool models.DeployTool if err := h.db.First(&tool, c.Param("id")); err.Error != nil { c.JSON(http.StatusNotFound, models.Response{Code: 404, Message: "工具不存在"}) return } c.JSON(http.StatusOK, models.Response{Code: 200, Data: tool}) } // UpdateTool updates a tool's configuration func (h *ToolConfigHandler) UpdateTool(c *gin.Context) { var tool models.DeployTool if err := h.db.First(&tool, c.Param("id")); err.Error != nil { c.JSON(http.StatusNotFound, models.Response{Code: 404, Message: "工具不存在"}) return } var req struct { DisplayName *string `json:"display_name"` Description *string `json:"description"` InstallMethod *models.ToolInstallMethod `json:"install_method"` Enabled *bool `json:"enabled"` SortOrder *int `json:"sort_order"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: err.Error()}) return } updates := map[string]interface{}{} if req.DisplayName != nil { updates["display_name"] = *req.DisplayName } if req.Description != nil { updates["description"] = *req.Description } if req.InstallMethod != nil { updates["install_method"] = *req.InstallMethod if *req.InstallMethod == models.ToolDisabled { updates["status"] = models.ToolNotInstalled } } if req.Enabled != nil { updates["enabled"] = *req.Enabled } if req.SortOrder != nil { updates["sort_order"] = *req.SortOrder } if len(updates) > 0 { h.db.Model(&tool).Updates(updates) } c.JSON(http.StatusOK, models.Response{Code: 200, Message: "已更新"}) } // InstallTool triggers lazy installation with optional version override func (h *ToolConfigHandler) InstallTool(c *gin.Context) { var tool models.DeployTool if err := h.db.First(&tool, c.Param("id")); err.Error != nil { c.JSON(http.StatusNotFound, models.Response{Code: 404, Message: "工具不存在"}) return } if !tool.Enabled { c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: "工具已禁用"}) return } if tool.InstallMethod != models.ToolLazy { c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: "仅懒加载工具可手动安装"}) return } // Support version override via query param or JSON body version := tool.DefaultVersion if v := c.Query("version"); v != "" { version = v } if v := c.PostForm("version"); v != "" { version = v } // Also support JSON body var req struct { Version string `json:"version"` } if err := c.ShouldBindJSON(&req); err == nil && req.Version != "" { version = req.Version } // Store the requested version for the installer if version != "" && version != tool.DefaultVersion { h.db.Model(&tool).Update("default_version", version) tool.DefaultVersion = version } go func() { log.Printf("[ToolConfig] Background install triggered for %s (version: %s)", tool.Name, version) }() c.JSON(http.StatusOK, models.Response{Code: 200, Message: "安装已触发", Data: gin.H{"version": version}}) } // CheckToolStatus re-checks a tool's installation status func (h *ToolConfigHandler) CheckToolStatus(c *gin.Context) { var tool models.DeployTool if err := h.db.First(&tool, c.Param("id")); err.Error != nil { c.JSON(http.StatusNotFound, models.Response{Code: 404, Message: "工具不存在"}) return } available := false version := "" if tool.CheckCommand != "" { cmd := exec.Command("sh", "-c", tool.CheckCommand) out, err := cmd.Output() if err == nil { available = true version = string(out) if len(version) > 128 { version = version[:128] } } } status := models.ToolNotInstalled if available { status = models.ToolInstalled } h.db.Model(&tool).Updates(map[string]interface{}{ "status": status, "installed_version": version, }) c.JSON(http.StatusOK, models.Response{Code: 200, Data: gin.H{ "available": available, "version": version, "tool": tool, }}) } // GetToolVersions returns available versions for a tool (jdk/node/maven) func (h *ToolConfigHandler) GetToolVersions(c *gin.Context) { var tool models.DeployTool if err := h.db.First(&tool, c.Param("id")); err.Error != nil { c.JSON(http.StatusNotFound, models.Response{Code: 404, Message: "工具不存在"}) return } versions := getPresetVersions(tool.Name) c.JSON(http.StatusOK, models.Response{Code: 200, Data: gin.H{ "name": tool.Name, "default": tool.DefaultVersion, "versions": versions, }}) } // getPresetVersions returns known versions for common tools func getPresetVersions(toolName string) []map[string]string { switch toolName { case "jdk": return []map[string]string{ {"version": "11.0.24+8", "label": "JDK 11 (LTS)"}, {"version": "17.0.13+7", "label": "JDK 17 (LTS) ★ 推荐"}, {"version": "21.0.5+11", "label": "JDK 21 (LTS)"}, {"version": "23.0.1+11", "label": "JDK 23 (最新)"}, } case "nodejs": return []map[string]string{ {"version": "v18.20.5", "label": "Node 18 (LTS)"}, {"version": "v20.18.1", "label": "Node 20 (LTS)"}, {"version": "v22.14.0", "label": "Node 22 (LTS) ★ 推荐"}, } case "maven": return []map[string]string{ {"version": "3.9.9", "label": "Maven 3.9.9 ★ 推荐"}, {"version": "3.8.8", "label": "Maven 3.8.8"}, {"version": "3.6.3", "label": "Maven 3.6.3"}, } default: return nil } }