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
@@ -12,7 +12,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"ezops/internal/models"
|
||||
"ezops/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -49,7 +49,7 @@ func (ti *ToolInstaller) ensureOnce(name string) *sync.Once {
|
||||
// Used by the manual "安装" button in the tool config UI.
|
||||
// The version parameter overrides DefaultVersion if non-empty.
|
||||
func (ti *ToolInstaller) ForceInstall(toolName string, version string) error {
|
||||
var tool models.DeployTool
|
||||
var tool model.DeployTool
|
||||
if err := ti.db.Where("name = ?", toolName).First(&tool).Error; err != nil {
|
||||
return fmt.Errorf("tool %s not found in DB: %w", toolName, err)
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func (ti *ToolInstaller) ForceInstall(toolName string, version string) error {
|
||||
// Returns nil if the tool is ready, or an error if installation failed.
|
||||
func (ti *ToolInstaller) EnsureAvailable(toolName string) error {
|
||||
// Find tool in DB
|
||||
var tool models.DeployTool
|
||||
var tool model.DeployTool
|
||||
if err := ti.db.Where("name = ?", toolName).First(&tool).Error; err != nil {
|
||||
// Tool not in DB — try to find it by path
|
||||
if _, lookErr := exec.LookPath(toolName); lookErr == nil {
|
||||
@@ -81,22 +81,22 @@ func (ti *ToolInstaller) EnsureAvailable(toolName string) error {
|
||||
}
|
||||
|
||||
// Check if already installed
|
||||
if tool.InstallMethod == models.ToolPreinstall || tool.InstallMethod == models.ToolLazy {
|
||||
if tool.InstallMethod == model.ToolPreinstall || tool.InstallMethod == model.ToolLazy {
|
||||
if inst, err := ti.checkInstalled(tool); err == nil {
|
||||
// Update status if needed
|
||||
if tool.Status != models.ToolInstalled {
|
||||
ti.updateStatus(tool.ID, models.ToolInstalled, inst, "")
|
||||
if tool.Status != model.ToolInstalled {
|
||||
ti.updateStatus(tool.ID, model.ToolInstalled, inst, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch tool.InstallMethod {
|
||||
case models.ToolPreinstall:
|
||||
case model.ToolPreinstall:
|
||||
return fmt.Errorf("tool %s should be preinstalled but not found", toolName)
|
||||
case models.ToolDisabled:
|
||||
case model.ToolDisabled:
|
||||
return fmt.Errorf("tool %s is disabled", toolName)
|
||||
case models.ToolLazy:
|
||||
case model.ToolLazy:
|
||||
// Use sync.Once to prevent concurrent installs
|
||||
var installErr error
|
||||
once := ti.ensureOnce(toolName)
|
||||
@@ -116,7 +116,7 @@ func (ti *ToolInstaller) EnsureAvailable(toolName string) error {
|
||||
}
|
||||
|
||||
// checkInstalled runs the tool's check command and returns the version string.
|
||||
func (ti *ToolInstaller) checkInstalled(tool models.DeployTool) (string, error) {
|
||||
func (ti *ToolInstaller) checkInstalled(tool model.DeployTool) (string, error) {
|
||||
// First try LookPath
|
||||
if tool.BinaryName != "" {
|
||||
if _, err := exec.LookPath(tool.BinaryName); err != nil {
|
||||
@@ -138,11 +138,11 @@ func (ti *ToolInstaller) checkInstalled(tool models.DeployTool) (string, error)
|
||||
}
|
||||
|
||||
// installTool performs the actual installation.
|
||||
func (ti *ToolInstaller) installTool(tool *models.DeployTool) error {
|
||||
func (ti *ToolInstaller) installTool(tool *model.DeployTool) error {
|
||||
log.Printf("[ToolInstaller] Installing %s (method=%s)...", tool.Name, tool.InstallMethod)
|
||||
|
||||
// Mark as installing
|
||||
ti.updateStatus(tool.ID, models.ToolInstalling, "", "")
|
||||
ti.updateStatus(tool.ID, model.ToolInstalling, "", "")
|
||||
|
||||
var err error
|
||||
if tool.InstallScript != "" {
|
||||
@@ -156,7 +156,7 @@ func (ti *ToolInstaller) installTool(tool *models.DeployTool) error {
|
||||
if err != nil {
|
||||
errMsg := err.Error()
|
||||
log.Printf("[ToolInstaller] ERROR installing %s: %s", tool.Name, errMsg)
|
||||
ti.updateStatus(tool.ID, models.ToolFailed, "", errMsg)
|
||||
ti.updateStatus(tool.ID, model.ToolFailed, "", errMsg)
|
||||
return fmt.Errorf("failed to install %s: %w", tool.Name, err)
|
||||
}
|
||||
|
||||
@@ -164,17 +164,17 @@ func (ti *ToolInstaller) installTool(tool *models.DeployTool) error {
|
||||
ver, checkErr := ti.checkInstalled(*tool)
|
||||
if checkErr != nil {
|
||||
log.Printf("[ToolInstaller] WARNING: %s installed but check failed: %v", tool.Name, checkErr)
|
||||
ti.updateStatus(tool.ID, models.ToolInstalled, "unknown", "")
|
||||
ti.updateStatus(tool.ID, model.ToolInstalled, "unknown", "")
|
||||
} else {
|
||||
log.Printf("[ToolInstaller] %s installed successfully: %s", tool.Name, ver)
|
||||
ti.updateStatus(tool.ID, models.ToolInstalled, ver, "")
|
||||
ti.updateStatus(tool.ID, model.ToolInstalled, ver, "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// runInstallScript executes the tool's install script in a subshell.
|
||||
func (ti *ToolInstaller) runInstallScript(tool *models.DeployTool) error {
|
||||
func (ti *ToolInstaller) runInstallScript(tool *model.DeployTool) error {
|
||||
// Write script to temp file
|
||||
tmpFile, err := os.CreateTemp("", fmt.Sprintf("tool-install-%s-*.sh", tool.Name))
|
||||
if err != nil {
|
||||
@@ -203,7 +203,7 @@ func (ti *ToolInstaller) runInstallScript(tool *models.DeployTool) error {
|
||||
// downloadBinary downloads a binary file to the install path.
|
||||
// Uses multi-mirror fallback: tries each mirror URL in order, with HEAD verification
|
||||
// to skip dead mirrors before attempting a full download.
|
||||
func (ti *ToolInstaller) downloadBinary(tool *models.DeployTool) error {
|
||||
func (ti *ToolInstaller) downloadBinary(tool *model.DeployTool) error {
|
||||
version := tool.DefaultVersion
|
||||
if version == "" {
|
||||
// Try to get latest version
|
||||
@@ -244,7 +244,7 @@ func (ti *ToolInstaller) downloadBinary(tool *models.DeployTool) error {
|
||||
}
|
||||
|
||||
// tryDownload attempts a single download from a URL.
|
||||
func (ti *ToolInstaller) tryDownload(url string, tool *models.DeployTool) error {
|
||||
func (ti *ToolInstaller) tryDownload(url string, tool *model.DeployTool) error {
|
||||
// Use a client with timeout for the download phase.
|
||||
// Large files (kubectl ~50MB) can take minutes on slow networks.
|
||||
client := &http.Client{Timeout: 30 * time.Minute}
|
||||
@@ -307,7 +307,7 @@ func (ti *ToolInstaller) fetchVersion(url string) (string, error) {
|
||||
}
|
||||
|
||||
// updateStatus updates the tool's status in the database.
|
||||
func (ti *ToolInstaller) updateStatus(toolID uint, status models.ToolStatus, version string, errMsg string) {
|
||||
func (ti *ToolInstaller) updateStatus(toolID uint, status model.ToolStatus, version string, errMsg string) {
|
||||
now := time.Now()
|
||||
updates := map[string]interface{}{
|
||||
"status": status,
|
||||
@@ -319,29 +319,29 @@ func (ti *ToolInstaller) updateStatus(toolID uint, status models.ToolStatus, ver
|
||||
if errMsg != "" {
|
||||
updates["error_msg"] = errMsg
|
||||
}
|
||||
ti.db.Model(&models.DeployTool{}).Where("id = ?", toolID).Updates(updates)
|
||||
ti.db.Model(&model.DeployTool{}).Where("id = ?", toolID).Updates(updates)
|
||||
}
|
||||
|
||||
// SeedTools inserts predefined tools into the database if the table is empty.
|
||||
func (ti *ToolInstaller) SeedTools() {
|
||||
var count int64
|
||||
ti.db.Model(&models.DeployTool{}).Count(&count)
|
||||
ti.db.Model(&model.DeployTool{}).Count(&count)
|
||||
if count > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tools := models.PredefinedTools()
|
||||
tools := model.PredefinedTools()
|
||||
now := time.Now()
|
||||
for i := range tools {
|
||||
tools[i].CreatedAt = now
|
||||
tools[i].UpdatedAt = now
|
||||
// Check if preinstalled tools are actually available
|
||||
if tools[i].InstallMethod == models.ToolPreinstall {
|
||||
if tools[i].InstallMethod == model.ToolPreinstall {
|
||||
if ver, err := ti.checkInstalled(tools[i]); err == nil {
|
||||
tools[i].Status = models.ToolInstalled
|
||||
tools[i].Status = model.ToolInstalled
|
||||
tools[i].InstalledVer = ver
|
||||
} else {
|
||||
tools[i].Status = models.ToolNotInstalled
|
||||
tools[i].Status = model.ToolNotInstalled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user