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

+92 -3
View File
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"math"
"strconv"
"strings"
"time"
@@ -230,7 +231,7 @@ func getRedisMetrics(instance *model.Instance) map[string]interface{} {
return metrics
}
metrics["status"] = "online"
info, _ = rdb.Info(ctx, "server", "clients", "stats").Result()
info, _ = rdb.Info(ctx, "server", "clients", "stats", "memory", "cpu").Result()
// Latency from PING
start := time.Now()
rdb.Ping(ctx)
@@ -274,6 +275,48 @@ func getRedisMetrics(instance *model.Instance) map[string]interface{} {
metrics["uptime"] = fmt.Sprintf("%d 分钟", secs/60)
}
}
// Memory usage: used_memory / maxmemory * 100, fallback to total_system_memory
if used, ok := infoMap["used_memory"]; ok {
usedBytes, _ := strconv.ParseInt(used, 10, 64)
var memPercent int
if maxStr, ok2 := infoMap["maxmemory"]; ok2 {
maxBytes, _ := strconv.ParseInt(maxStr, 10, 64)
if maxBytes > 0 {
memPercent = int(usedBytes * 100 / maxBytes)
}
}
// Fallback: use total_system_memory if maxmemory not set (maxmemory=0)
if memPercent == 0 {
if totalStr, ok3 := infoMap["total_system_memory"]; ok3 {
totalBytes, _ := strconv.ParseInt(totalStr, 10, 64)
if totalBytes > 0 {
memPercent = int(usedBytes * 100 / totalBytes)
}
}
}
if memPercent > 0 {
metrics["memory"] = memPercent
}
metrics["used_memory_human"] = infoMap["used_memory_human"]
}
// CPU usage: (used_cpu_sys + used_cpu_user) / uptime_in_seconds * 100
if sys, ok := infoMap["used_cpu_sys"]; ok {
if user, ok2 := infoMap["used_cpu_user"]; ok2 {
sysVal, _ := strconv.ParseFloat(sys, 64)
userVal, _ := strconv.ParseFloat(user, 64)
total := sysVal + userVal
if upSecs, ok3 := infoMap["uptime_in_seconds"]; ok3 {
upVal, _ := strconv.ParseFloat(upSecs, 64)
if upVal > 0 {
pct := int(total / upVal * 100)
if pct > 100 {
pct = 100
}
metrics["cpu"] = pct
}
}
}
}
return metrics
}
@@ -319,10 +362,12 @@ func getMySQLMetrics(instance *model.Instance) map[string]interface{} {
metrics["latency"] = time.Since(start).Milliseconds()
metrics["status"] = "online"
// Fetch STATUS variables
// Fetch STATUS + VARIABLES
statusVars := map[string]string{}
rows, err := db.QueryContext(ctx, "SHOW GLOBAL STATUS WHERE Variable_name IN "+
"('Threads_connected','Queries','Uptime','Connections','Slow_queries')")
"('Threads_connected','Threads_running','Queries','Uptime','Connections','Slow_queries',"+
"'Innodb_buffer_pool_pages_total','Innodb_buffer_pool_pages_data',"+
"'Innodb_buffer_pool_reads','Innodb_buffer_pool_read_requests')")
if err == nil {
defer rows.Close()
for rows.Next() {
@@ -331,10 +376,33 @@ func getMySQLMetrics(instance *model.Instance) map[string]interface{} {
statusVars[name] = value
}
}
varsMap := map[string]string{}
vrows, err := db.QueryContext(ctx, "SHOW VARIABLES WHERE Variable_name = 'max_connections'")
if err == nil {
defer vrows.Close()
for vrows.Next() {
var name, value string
vrows.Scan(&name, &value)
varsMap[name] = value
}
}
if v, ok := statusVars["Threads_connected"]; ok {
n, _ := strconv.Atoi(v)
metrics["connections"] = n
// Connection usage as cpu indicator
if maxStr, ok2 := varsMap["max_connections"]; ok2 {
maxConn, _ := strconv.Atoi(maxStr)
if maxConn > 0 {
metrics["cpu"] = int(math.Ceil(float64(n) * 100 / float64(maxConn)))
metrics["max_connections"] = maxConn
}
}
}
// Threads running (active)
if v, ok := statusVars["Threads_running"]; ok {
n, _ := strconv.Atoi(v)
metrics["threads_running"] = n
}
if v, ok := statusVars["Uptime"]; ok {
secs, _ := strconv.Atoi(v)
@@ -356,6 +424,27 @@ func getMySQLMetrics(instance *model.Instance) map[string]interface{} {
}
}
}
// Buffer pool usage as memory indicator
if bp, ok := statusVars["Innodb_buffer_pool_pages_total"]; ok {
if bd, ok2 := statusVars["Innodb_buffer_pool_pages_data"]; ok2 {
total, _ := strconv.ParseFloat(bp, 64)
used, _ := strconv.ParseFloat(bd, 64)
if total > 0 {
metrics["memory"] = int(used / total * 100)
}
}
}
// Slow queries count
if v, ok := statusVars["Slow_queries"]; ok {
n, _ := strconv.Atoi(v)
metrics["slow_queries"] = n
}
// Data size on disk
var dataMB float64
db.QueryRowContext(ctx, "SELECT COALESCE(ROUND(SUM(data_length + index_length)/1024/1024, 1), 0) FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys')").Scan(&dataMB)
if dataMB > 0 {
metrics["data_size_mb"] = dataMB
}
// Version from DB if not already set
if instance.Version == nil || *instance.Version == "" {