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
+28
-28
@@ -5,8 +5,8 @@ import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"ezops/internal/config"
|
||||
"ezops/internal/models"
|
||||
"ezops/config"
|
||||
"ezops/model"
|
||||
"ezops/pkg/database"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@@ -77,7 +77,7 @@ func generateMockData(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateUsers(db *gorm.DB) error {
|
||||
users := []models.User{
|
||||
users := []model.User{
|
||||
{Username: "admin", Email: "admin@ezops.local", RealName: "系统管理员", Phone: "13800138000", Status: 1, IsAdmin: true},
|
||||
{Username: "ops01", Email: "ops01@ezops.local", RealName: "运维工程师1", Phone: "13800138001", Status: 1, IsAdmin: false},
|
||||
{Username: "ops02", Email: "ops02@ezops.local", RealName: "运维工程师2", Phone: "13800138002", Status: 1, IsAdmin: false},
|
||||
@@ -100,9 +100,9 @@ func generateServers(db *gorm.DB) error {
|
||||
osTypes := []string{"CentOS 7.9", "CentOS 8.5", "Ubuntu 20.04", "Ubuntu 22.04", "Debian 11", "Rocky Linux 8.6"}
|
||||
statuses := []string{"running", "stopped", "maintenance"}
|
||||
|
||||
servers := make([]models.Server, 0, 50)
|
||||
servers := make([]model.Server, 0, 50)
|
||||
for i := 0; i < 50; i++ {
|
||||
server := models.Server{
|
||||
server := model.Server{
|
||||
Hostname: fmt.Sprintf("server-%03d", i+1),
|
||||
IP: fmt.Sprintf("10.0.%d.%d", rand.Intn(256), rand.Intn(256)),
|
||||
PublicIP: fmt.Sprintf("47.%d.%d.%d", rand.Intn(256), rand.Intn(256), rand.Intn(256)),
|
||||
@@ -127,7 +127,7 @@ func generateServers(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateCloudAccounts(db *gorm.DB) error {
|
||||
accounts := []models.CloudAccount{
|
||||
accounts := []model.CloudAccount{
|
||||
{Name: "阿里云主账号", Provider: "aliyun", Status: "active", MonthlyCost: 28500},
|
||||
{Name: "腾讯云主账号", Provider: "tencent", Status: "active", MonthlyCost: 15800},
|
||||
{Name: "AWS 账号", Provider: "aws", Status: "active", MonthlyCost: 42000},
|
||||
@@ -145,7 +145,7 @@ func generateCloudAccounts(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateDomains(db *gorm.DB) error {
|
||||
domains := []models.Domain{
|
||||
domains := []model.Domain{
|
||||
{Name: "ezops.local", Registrar: "aliyun", Status: "active", DNSProvider: "alidns"},
|
||||
{Name: "api.ezops.local", Registrar: "aliyun", Status: "active", DNSProvider: "alidns"},
|
||||
{Name: "app.ezops.local", Registrar: "tencent", Status: "active", DNSProvider: "dnspod"},
|
||||
@@ -165,7 +165,7 @@ func generateDomains(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateKubernetesClusters(db *gorm.DB) error {
|
||||
clusters := []models.KubernetesCluster{
|
||||
clusters := []model.KubernetesCluster{
|
||||
{Name: "prod-cluster", Code: "prod", Version: "v1.28.0", Status: "healthy", NodeCount: 10, PodCount: 150, ServiceCount: 30, DeploymentCount: 15, APIEndpoint: "https://prod-api.ezops.local:6443", CPUUsage: 65.5, MemoryUsage: 72.3, Description: "生产环境 Kubernetes 集群"},
|
||||
{Name: "staging-cluster", Code: "staging", Version: "v1.28.0", Status: "healthy", NodeCount: 5, PodCount: 80, ServiceCount: 15, DeploymentCount: 10, APIEndpoint: "https://staging-api.ezops.local:6443", CPUUsage: 45.2, MemoryUsage: 55.0, Description: "预发环境 Kubernetes 集群"},
|
||||
{Name: "dev-cluster", Code: "dev", Version: "v1.27.0", Status: "warning", NodeCount: 3, PodCount: 45, ServiceCount: 8, DeploymentCount: 8, APIEndpoint: "https://dev-api.ezops.local:6443", CPUUsage: 80.1, MemoryUsage: 85.0, Description: "开发环境 Kubernetes 集群"},
|
||||
@@ -179,7 +179,7 @@ func generateKubernetesClusters(db *gorm.DB) error {
|
||||
for _, cluster := range clusters {
|
||||
// 生成节点
|
||||
for j := 0; j < cluster.NodeCount; j++ {
|
||||
node := models.KubernetesNode{
|
||||
node := model.KubernetesNode{
|
||||
ClusterID: cluster.ID,
|
||||
Name: fmt.Sprintf("%s-node-%d", cluster.Code, j+1),
|
||||
IP: fmt.Sprintf("192.168.%d.%d", rand.Intn(256), rand.Intn(256)),
|
||||
@@ -227,7 +227,7 @@ func generateKubernetesClusters(db *gorm.DB) error {
|
||||
if available < 0 {
|
||||
available = 0
|
||||
}
|
||||
dep := models.KubernetesDeployment{
|
||||
dep := model.KubernetesDeployment{
|
||||
ClusterID: cluster.ID,
|
||||
Namespace: namespaces[rand.Intn(len(namespaces))],
|
||||
Name: depNames[d],
|
||||
@@ -253,7 +253,7 @@ func generateKubernetesClusters(db *gorm.DB) error {
|
||||
}
|
||||
for p := 0; p < numPods; p++ {
|
||||
status := podStatuses[rand.Intn(len(podStatuses))]
|
||||
pod := models.KubernetesPod{
|
||||
pod := model.KubernetesPod{
|
||||
ClusterID: cluster.ID,
|
||||
Namespace: namespaces[rand.Intn(len(namespaces))],
|
||||
Name: fmt.Sprintf("%s-pod-%d", cluster.Code, p+1),
|
||||
@@ -297,7 +297,7 @@ func generateKubernetesClusters(db *gorm.DB) error {
|
||||
default:
|
||||
ports = `[{"port":3306,"protocol":"TCP","targetPort":3306}]`
|
||||
}
|
||||
svc := models.KubernetesService{
|
||||
svc := model.KubernetesService{
|
||||
ClusterID: cluster.ID,
|
||||
Namespace: namespaces[rand.Intn(len(namespaces))],
|
||||
Name: svcNames[s],
|
||||
@@ -323,7 +323,7 @@ func generateKubernetesClusters(db *gorm.DB) error {
|
||||
numIngs := 2 + rand.Intn(3)
|
||||
for ig := 0; ig < numIngs && ig < len(ingNames); ig++ {
|
||||
hosts := fmt.Sprintf(`["%s.ezops.local"]`, ingNames[ig])
|
||||
ing := models.KubernetesIngress{
|
||||
ing := model.KubernetesIngress{
|
||||
ClusterID: cluster.ID,
|
||||
Namespace: namespaces[rand.Intn(len(namespaces))],
|
||||
Name: ingNames[ig],
|
||||
@@ -341,7 +341,7 @@ func generateKubernetesClusters(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateAlertRules(db *gorm.DB) error {
|
||||
rules := []models.AlertRule{
|
||||
rules := []model.AlertRule{
|
||||
{Name: "CPU 使用率告警", Type: "metric", Source: "prometheus", Expression: "cpu_usage > 80", Severity: "warning", Duration: 300, Enabled: true, Description: "CPU 使用率超过 80%"},
|
||||
{Name: "CPU 使用率严重告警", Type: "metric", Source: "prometheus", Expression: "cpu_usage > 95", Severity: "critical", Duration: 120, Enabled: true, Description: "CPU 使用率超过 95%"},
|
||||
{Name: "内存使用率告警", Type: "metric", Source: "prometheus", Expression: "memory_usage > 80", Severity: "warning", Duration: 300, Enabled: true, Description: "内存使用率超过 80%"},
|
||||
@@ -356,7 +356,7 @@ func generateAlertRules(db *gorm.DB) error {
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
rule := rules[rand.Intn(len(rules))]
|
||||
event := models.AlertEvent{
|
||||
event := model.AlertEvent{
|
||||
RuleID: rule.ID,
|
||||
RuleName: rule.Name,
|
||||
Severity: rule.Severity,
|
||||
@@ -375,7 +375,7 @@ func generateAlertRules(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateNotifyChannels(db *gorm.DB) error {
|
||||
channels := []models.NotifyChannel{
|
||||
channels := []model.NotifyChannel{
|
||||
{Name: "运维邮件通知", Type: "email", Config: `{"recipients":["ops@ezops.com","admin@ezops.com"],"subject":"[AIOps告警]"}`, Enabled: true, TestStatus: "success"},
|
||||
{Name: "短信通知", Type: "sms", Config: `{"phones":["13800138000","13900139000"],"template":"告警:{{alert_name}}"}`, Enabled: true, TestStatus: "success"},
|
||||
{Name: "钉钉机器人", Type: "dingtalk", Config: `{"webhook":"https://oapi.dingtalk.com/robot/send?access_token=eZOps***","secret":"SEC***","at_all":false}`, Enabled: true, TestStatus: "success"},
|
||||
@@ -392,7 +392,7 @@ func generateNotifyChannels(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generatePipelines(db *gorm.DB) error {
|
||||
pipelines := []models.Pipeline{
|
||||
pipelines := []model.Pipeline{
|
||||
{Name: "前端构建", Code: "frontend-build", Type: "build", Source: "https://gitlab.com/ezops/frontend", Branch: "main", Enabled: true, Description: "前端项目构建流水线"},
|
||||
{Name: "后端构建", Code: "backend-build", Type: "build", Source: "https://gitlab.com/ezops/backend", Branch: "main", Enabled: true, Description: "后端项目构建流水线"},
|
||||
{Name: "部署预发", Code: "deploy-staging", Type: "deploy", Source: "https://gitlab.com/ezops/deploy", Branch: "staging", Enabled: true, Description: "部署到预发环境"},
|
||||
@@ -406,7 +406,7 @@ func generatePipelines(db *gorm.DB) error {
|
||||
}
|
||||
for _, pipeline := range pipelines {
|
||||
for j := 0; j < 5; j++ {
|
||||
run := models.PipelineRun{
|
||||
run := model.PipelineRun{
|
||||
PipelineID: pipeline.ID,
|
||||
BuildNumber: j + 1,
|
||||
Status: []string{"success", "success", "success", "failed", "running"}[rand.Intn(5)],
|
||||
@@ -431,7 +431,7 @@ func generateAppReleases(db *gorm.DB) error {
|
||||
statuses := []string{"pending", "building", "deploying", "success", "success", "success", "failed", "rolled_back"}
|
||||
authors := []string{"admin", "ops01", "dev01"}
|
||||
|
||||
releases := make([]models.AppRelease, 0, 10)
|
||||
releases := make([]model.AppRelease, 0, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
app := apps[i%len(apps)]
|
||||
env := environments[rand.Intn(len(environments))]
|
||||
@@ -439,7 +439,7 @@ func generateAppReleases(db *gorm.DB) error {
|
||||
minor := rand.Intn(20)
|
||||
patch := rand.Intn(10)
|
||||
|
||||
release := models.AppRelease{
|
||||
release := model.AppRelease{
|
||||
AppName: app,
|
||||
Version: fmt.Sprintf("v1.%d.%d", minor, patch),
|
||||
Environment: env,
|
||||
@@ -486,7 +486,7 @@ func generateAppReleases(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateCronJobs(db *gorm.DB) error {
|
||||
cronJobs := []models.CronJob{
|
||||
cronJobs := []model.CronJob{
|
||||
{Name: "数据库备份", CronExpr: "0 2 * * *", Type: "script", Content: "/scripts/backup-db.sh", Enabled: true, Description: "每日凌晨 2 点备份数据库"},
|
||||
{Name: "清理日志", CronExpr: "0 3 * * 0", Type: "command", Content: "find /var/log -mtime +30 -delete", Enabled: true, Description: "每周日凌晨 3 点清理日志"},
|
||||
{Name: "数据同步", CronExpr: "*/30 * * * *", Type: "script", Content: "/scripts/sync-data.sh", Enabled: true, Description: "每 30 分钟同步数据"},
|
||||
@@ -503,7 +503,7 @@ func generateCronJobs(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateScripts(db *gorm.DB) error {
|
||||
scripts := []models.Script{
|
||||
scripts := []model.Script{
|
||||
{Name: "重启服务", Code: "restart-service", Type: "shell", Content: "#!/bin/bash\nsystemctl restart $1", Description: "重启服务脚本"},
|
||||
{Name: "检查磁盘", Code: "check-disk", Type: "shell", Content: "#!/bin/bash\ndf -h", Description: "检查磁盘空间"},
|
||||
{Name: "检查内存", Code: "check-memory", Type: "shell", Content: "#!/bin/bash\nfree -h", Description: "检查内存使用"},
|
||||
@@ -520,7 +520,7 @@ func generateScripts(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateApprovalTemplates(db *gorm.DB) error {
|
||||
templates := []models.ApprovalTemplate{
|
||||
templates := []model.ApprovalTemplate{
|
||||
{Name: "服务器申请审批", Code: "server-request", Type: "server", Enabled: true, Description: "服务器资源申请审批流程"},
|
||||
{Name: "发布审批", Code: "release-approval", Type: "release", Enabled: true, Description: "应用发布审批流程"},
|
||||
{Name: "权限申请审批", Code: "permission-request", Type: "permission", Enabled: true, Description: "系统权限申请审批流程"},
|
||||
@@ -535,7 +535,7 @@ func generateApprovalTemplates(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
func generateNavigations(db *gorm.DB) error {
|
||||
groups := []models.NavigationGroup{
|
||||
groups := []model.NavigationGroup{
|
||||
{Name: "常用工具", Code: "tools", Icon: "Tool", Sort: 1, Enabled: true},
|
||||
{Name: "监控平台", Code: "monitor", Icon: "Activity", Sort: 2, Enabled: true},
|
||||
{Name: "开发工具", Code: "devtools", Icon: "Code", Sort: 3, Enabled: true},
|
||||
@@ -546,7 +546,7 @@ func generateNavigations(db *gorm.DB) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
links := []models.NavigationLink{
|
||||
links := []model.NavigationLink{
|
||||
{GroupID: groups[0].ID, Name: "GitLab", URL: "https://gitlab.com", Icon: "GitBranch", Description: "代码仓库", Sort: 1},
|
||||
{GroupID: groups[0].ID, Name: "Jenkins", URL: "https://jenkins.io", Icon: "Settings", Description: "CI/CD 工具", Sort: 2},
|
||||
{GroupID: groups[0].ID, Name: "Nexus", URL: "https://www.sonatype.com/nexus-repository-oss", Icon: "Package", Description: "制品库", Sort: 3},
|
||||
@@ -587,7 +587,7 @@ func generateBillings(db *gorm.DB) error {
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
var billings []models.CloudBilling
|
||||
var billings []model.CloudBilling
|
||||
|
||||
// 生成最近60天每天的账单
|
||||
for day := 59; day >= 0; day-- {
|
||||
@@ -599,7 +599,7 @@ func generateBillings(db *gorm.DB) error {
|
||||
for i := 0; i < count; i++ {
|
||||
svc := svcs[rand.Intn(len(svcs))]
|
||||
cost := 50 + rand.Float64()*500
|
||||
billings = append(billings, models.CloudBilling{
|
||||
billings = append(billings, model.CloudBilling{
|
||||
CloudAccountID: &acc.ID,
|
||||
AccountName: acc.Name,
|
||||
Provider: acc.Provider,
|
||||
@@ -621,7 +621,7 @@ func generateBillings(db *gorm.DB) error {
|
||||
fmt.Println("Generated cloud billings:", len(billings))
|
||||
|
||||
// 生成预警规则
|
||||
rules := []models.BillingAlertRule{
|
||||
rules := []model.BillingAlertRule{
|
||||
{Name: "每日总成本超500元", Metric: "daily", Threshold: 500, Operator: ">", Enabled: true},
|
||||
{Name: "阿里云日费用超300元", Provider: "aliyun", Metric: "daily", Threshold: 300, Operator: ">", Enabled: true},
|
||||
{Name: "月度总成本超1万元", Metric: "monthly", Threshold: 10000, Operator: ">", Enabled: true},
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"ezops/internal/config"
|
||||
"ezops/internal/models"
|
||||
"ezops/config"
|
||||
"ezops/model"
|
||||
"ezops/pkg/database"
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ func main() {
|
||||
}
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
var clusters []models.KubernetesCluster
|
||||
var clusters []model.KubernetesCluster
|
||||
db.Find(&clusters)
|
||||
if len(clusters) == 0 {
|
||||
fmt.Println("No clusters found — run mock.go first")
|
||||
@@ -54,7 +54,7 @@ func main() {
|
||||
for _, cluster := range clusters {
|
||||
// Skip if pods already exist for this cluster
|
||||
var podCount int64
|
||||
db.Model(&models.KubernetesPod{}).Where("cluster_id = ?", cluster.ID).Count(&podCount)
|
||||
db.Model(&model.KubernetesPod{}).Where("cluster_id = ?", cluster.ID).Count(&podCount)
|
||||
if podCount > 0 {
|
||||
fmt.Printf("Cluster %s already has %d pods, skipping\n", cluster.Name, podCount)
|
||||
continue
|
||||
@@ -79,7 +79,7 @@ func main() {
|
||||
if available < 0 {
|
||||
available = 0
|
||||
}
|
||||
db.Create(&models.KubernetesDeployment{
|
||||
db.Create(&model.KubernetesDeployment{
|
||||
ClusterID: cluster.ID,
|
||||
Namespace: namespaces[rand.Intn(len(namespaces))],
|
||||
Name: depNames[d],
|
||||
@@ -94,7 +94,7 @@ func main() {
|
||||
// Pods
|
||||
numPods := 12 + rand.Intn(18)
|
||||
for p := 0; p < numPods; p++ {
|
||||
db.Create(&models.KubernetesPod{
|
||||
db.Create(&model.KubernetesPod{
|
||||
ClusterID: cluster.ID,
|
||||
Namespace: namespaces[rand.Intn(len(namespaces))],
|
||||
Name: fmt.Sprintf("%s-pod-%d", cluster.Code, p+1),
|
||||
@@ -126,7 +126,7 @@ func main() {
|
||||
default:
|
||||
ports = `[{"port":3306,"protocol":"TCP","targetPort":3306}]`
|
||||
}
|
||||
svc := models.KubernetesService{
|
||||
svc := model.KubernetesService{
|
||||
ClusterID: cluster.ID,
|
||||
Namespace: namespaces[rand.Intn(len(namespaces))],
|
||||
Name: svcNames[s],
|
||||
@@ -150,7 +150,7 @@ func main() {
|
||||
numIngs = len(ingNames)
|
||||
}
|
||||
for ig := 0; ig < numIngs; ig++ {
|
||||
db.Create(&models.KubernetesIngress{
|
||||
db.Create(&model.KubernetesIngress{
|
||||
ClusterID: cluster.ID,
|
||||
Namespace: namespaces[rand.Intn(len(namespaces))],
|
||||
Name: ingNames[ig],
|
||||
@@ -162,9 +162,9 @@ func main() {
|
||||
|
||||
// Update cluster counts to match actual data
|
||||
var depCnt, podCnt, svcCnt int64
|
||||
db.Model(&models.KubernetesDeployment{}).Where("cluster_id = ?", cluster.ID).Count(&depCnt)
|
||||
db.Model(&models.KubernetesPod{}).Where("cluster_id = ?", cluster.ID).Count(&podCnt)
|
||||
db.Model(&models.KubernetesService{}).Where("cluster_id = ?", cluster.ID).Count(&svcCnt)
|
||||
db.Model(&model.KubernetesDeployment{}).Where("cluster_id = ?", cluster.ID).Count(&depCnt)
|
||||
db.Model(&model.KubernetesPod{}).Where("cluster_id = ?", cluster.ID).Count(&podCnt)
|
||||
db.Model(&model.KubernetesService{}).Where("cluster_id = ?", cluster.ID).Count(&svcCnt)
|
||||
db.Model(&cluster).Updates(map[string]interface{}{
|
||||
"deployment_count": depCnt,
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user