Files
aiops/backend/internal/api/handlers/tool_config.go
T
attacker aaab923f4e feat: K8s→Kubernetes命名统一 + 工具安装bug修复 + 资源可视化编辑器
1. 工具安装bug修复 (P0 critical):
   - 根因: ToolConfigHandler.InstallTool的goroutine只打了日志,
     从未调用实际安装逻辑
   - 修复: 注入ToolInstaller到handler, 新增ForceInstall方法绕过sync.Once
   - 影响: 流水线工具配置页「安装」按钮现在能真正触发下载安装

2. K8s→Kubernetes命名统一:
   - Dashboard/Prometheus/AIOpsAssistant等8个文件12处用户可见文案
   - 内部变量名/文件名/API group保持不变

3. 资源可视化编辑器 (新增ResourceVisualEditor.tsx):
   - ConfigMap: 键值对表格编辑器(增删改)
   - Secret: 键值对编辑器(自动Base64编解码)
   - Service: 端口/类型/选择器可视化配置
   - Ingress: Host/Path路由规则可视化编辑
   - PVC: 存储大小/访问模式/StorageClass配置
   - 双模式: 可视化编辑 ↔ YAML编辑一键切换
   - NamespaceResourcePage: 所有资源类型添加编辑按钮入口
2026-06-25 01:32:10 +08:00

229 lines
6.6 KiB
Go

package handlers
import (
"log"
"net/http"
"os/exec"
"ezops/internal/models"
"ezops/pkg/toolinstaller"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// ==================== 工具配置 CRUD ====================
// ToolConfigHandler handles deploy tool configuration
type ToolConfigHandler struct {
db *gorm.DB
installer *toolinstaller.ToolInstaller
}
// NewToolConfigHandler creates handler
func NewToolConfigHandler(db *gorm.DB, ti *toolinstaller.ToolInstaller) *ToolConfigHandler {
return &ToolConfigHandler{db: db, installer: ti}
}
// 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
}
// Mark as installing immediately
h.db.Model(&tool).Update("status", models.ToolInstalling)
// Background installation — actually call the installer
go func() {
log.Printf("[ToolConfig] Background install triggered for %s (version: %s)", tool.Name, version)
if err := h.installer.ForceInstall(tool.Name, version); err != nil {
log.Printf("[ToolConfig] ERROR installing %s: %v", tool.Name, err)
} else {
log.Printf("[ToolConfig] %s installed successfully", tool.Name)
}
}()
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
}
}