feat: 平台配置全面生效 — 配置策略接入实际逻辑

- query组: ExecuteQuery读取query_limit_max/query_timeout/query_dangerous_pattern
  - 查询超时通过context.WithTimeout生效,db.QueryContext/QueryRowContext
  - 危险模式检测拦截无WHERE的DELETE/UPDATE/DROP/TRUNCATE
  - LIMIT上限从settings读取,不再硬编码1000

- security组: Login读取login_max_attempts/login_lockout_minutes
  - 新增User字段: failed_login_attempts + locked_until
  - 登录失败达到阈值自动锁定,成功时清除计数
  - JWT Token过期时间读取session_timeout_hours设置

- executor组: ExecuteTicket读取executor_timeout/executor_dry_run
  - 执行超时通过context.WithTimeout控制
  - 开启dry_run时先EXPLAIN预检查SQL语法
  - db.ExecContext替代db.Exec

- cleanup组: SettingsCleanupPreview/Run从占位改为真实实现
  - 预览统计4类数据(工单/查询历史/慢日志/审计日志)待清理数量
  - 执行清理按retention_days配置删除过期数据

- ghost组: 13项gh-ost配置加入seedDefaultSettings
  - enabled/path/mode/osc_priority/env/cloud/trigger/chunk/load/lag等
  - 已被ghost.go中getGhostConfig等函数读取

- 前端: PlatformConfigPage新增Ghost/OSC分组显示
This commit is contained in:
logan committed 2026-06-01 01:00:23 +08:00
1 parent 8af868b3ca
commit bc2092a980
11 files changed
+365 -45

No files matched your search

+60 -2
View File
@@ -472,12 +472,70 @@ func UpdateSettings(c *gin.Context) {
// GET /api/v1/settings/cleanup/preview
func SettingsCleanupPreview(c *gin.Context) {
c.JSON(http.StatusOK, map[string]interface{}{})
now := time.Now()
stats := gin.H{}
// Tickets
ticketDays := getSettingInt("ticket_retention_days", 180)
var ticketCount int64
model.DB.Model(&model.Ticket{}).
Where("status IN ? AND create_time < ?", []string{"success", "failed", "closed", "rejected"}, now.AddDate(0, 0, -ticketDays)).
Count(&ticketCount)
stats["tickets"] = gin.H{"days": ticketDays, "count": ticketCount}
// Query history
queryDays := getSettingInt("query_history_retention_days", 90)
var queryCount int64
model.DB.Model(&model.QueryHistory{}).
Where("created_at < ?", now.AddDate(0, 0, -queryDays)).
Count(&queryCount)
stats["query_history"] = gin.H{"days": queryDays, "count": queryCount}
// Slow logs
slowDays := getSettingInt("slow_log_retention_days", 180)
var slowCount int64
model.DB.Model(&model.SlowLog{}).
Where("created_at < ?", now.AddDate(0, 0, -slowDays)).
Count(&slowCount)
stats["slow_logs"] = gin.H{"days": slowDays, "count": slowCount}
// Audit logs — skip if no AuditLog model; use raw SQL
auditDays := getSettingInt("audit_log_retention_days", 365)
var auditCount int64
model.DB.Raw("SELECT COUNT(*) FROM audit_logs WHERE created_at < ?", now.AddDate(0, 0, -auditDays)).Scan(&auditCount)
stats["audit_logs"] = gin.H{"days": auditDays, "count": auditCount}
c.JSON(http.StatusOK, stats)
}
// POST /api/v1/settings/cleanup/run
func SettingsCleanupRun(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "Cleanup done (placeholder)", "stats": map[string]interface{}{}})
now := time.Now()
stats := gin.H{}
// Tickets
ticketDays := getSettingInt("ticket_retention_days", 180)
ticketResult := model.DB.Where("status IN ? AND create_time < ?",
[]string{"success", "failed", "closed", "rejected"}, now.AddDate(0, 0, -ticketDays)).
Delete(&model.Ticket{})
stats["tickets_deleted"] = ticketResult.RowsAffected
// Query history
queryDays := getSettingInt("query_history_retention_days", 90)
queryResult := model.DB.Where("created_at < ?", now.AddDate(0, 0, -queryDays)).Delete(&model.QueryHistory{})
stats["query_history_deleted"] = queryResult.RowsAffected
// Slow logs
slowDays := getSettingInt("slow_log_retention_days", 180)
slowResult := model.DB.Where("created_at < ?", now.AddDate(0, 0, -slowDays)).Delete(&model.SlowLog{})
stats["slow_logs_deleted"] = slowResult.RowsAffected
// Audit logs
auditDays := getSettingInt("audit_log_retention_days", 365)
auditResult := model.DB.Exec("DELETE FROM audit_logs WHERE created_at < ?", now.AddDate(0, 0, -auditDays))
stats["audit_logs_deleted"] = auditResult.RowsAffected
c.JSON(http.StatusOK, gin.H{"message": "Cleanup completed", "stats": stats})
}
// GET /api/v1/notifications/channels