feat(audit): F1+F2 告警静默规则标记完成 + 审计日志CSV导出实现
F1 告警静默规则 [done]: - 后端 alert_silence.go: CRUD + Toggle (onetime/daily/weekly 三模式) - 告警引擎 isSilenced 集成 (alert_engine.go) - 前端 SilenceRuleTab + SilenceDialog - 路由 /alerts/silences 已注册 F2 审计日志导出 [done]: - 重写 ExportAuditLogs: 真正的 CSV 导出 (encoding/csv + UTF-8 BOM) - 支持 module/action/username/startTime/endTime 筛选 - 导出限制 10000 条防溢出,自动记录审计日志 - JSON 格式向后兼容 (format=json) - 前端 exportLogs 传递查询参数 同时包含累积的前期工作: - 目录重构 internal/ -> controller/model/router (v0.55-v0.57) - 后端分层架构 dto/repository/service/utils - 审计模块全功能 (终端审计/操作审计/登录日志) - asciicast v2 录制格式 - SFTP 文件浏览器 - StatStrip 紧凑统计条 - 在线会话自动刷新 - LiveMonitor WebSocket 诊断修复 验证: go build ✅ | npm run build ✅ | Docker 部署 ✅ | 10 回归端点 ✅ | CSV 导出 ✅ | 静默 CRUD ✅
This commit is contained in:
1 parent
e57718998a
commit
77c234b060
314 files changed
+10050
-4065
No files matched your search
@@ -0,0 +1,228 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
|
||||
"ezops/model"
|
||||
"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 []model.DeployTool
|
||||
h.db.Order("sort_order ASC").Find(&tools)
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Data: model.ListResponse{List: tools, Total: int64(len(tools))}})
|
||||
}
|
||||
|
||||
// GetTool returns a single tool
|
||||
func (h *ToolConfigHandler) GetTool(c *gin.Context) {
|
||||
var tool model.DeployTool
|
||||
if err := h.db.First(&tool, c.Param("id")); err.Error != nil {
|
||||
c.JSON(http.StatusNotFound, model.Response{Code: 404, Message: "工具不存在"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Data: tool})
|
||||
}
|
||||
|
||||
// UpdateTool updates a tool's configuration
|
||||
func (h *ToolConfigHandler) UpdateTool(c *gin.Context) {
|
||||
var tool model.DeployTool
|
||||
if err := h.db.First(&tool, c.Param("id")); err.Error != nil {
|
||||
c.JSON(http.StatusNotFound, model.Response{Code: 404, Message: "工具不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
DisplayName *string `json:"display_name"`
|
||||
Description *string `json:"description"`
|
||||
InstallMethod *model.ToolInstallMethod `json:"install_method"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
SortOrder *int `json:"sort_order"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.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 == model.ToolDisabled {
|
||||
updates["status"] = model.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, model.Response{Code: 200, Message: "已更新"})
|
||||
}
|
||||
|
||||
// InstallTool triggers lazy installation with optional version override
|
||||
func (h *ToolConfigHandler) InstallTool(c *gin.Context) {
|
||||
var tool model.DeployTool
|
||||
if err := h.db.First(&tool, c.Param("id")); err.Error != nil {
|
||||
c.JSON(http.StatusNotFound, model.Response{Code: 404, Message: "工具不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
if !tool.Enabled {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: "工具已禁用"})
|
||||
return
|
||||
}
|
||||
|
||||
if tool.InstallMethod != model.ToolLazy {
|
||||
c.JSON(http.StatusBadRequest, model.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", model.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, model.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 model.DeployTool
|
||||
if err := h.db.First(&tool, c.Param("id")); err.Error != nil {
|
||||
c.JSON(http.StatusNotFound, model.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 := model.ToolNotInstalled
|
||||
if available {
|
||||
status = model.ToolInstalled
|
||||
}
|
||||
h.db.Model(&tool).Updates(map[string]interface{}{
|
||||
"status": status,
|
||||
"installed_version": version,
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, model.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 model.DeployTool
|
||||
if err := h.db.First(&tool, c.Param("id")); err.Error != nil {
|
||||
c.JSON(http.StatusNotFound, model.Response{Code: 404, Message: "工具不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
versions := getPresetVersions(tool.Name)
|
||||
|
||||
c.JSON(http.StatusOK, model.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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user