Files
opsxsql/backend/cmd/server/seed.go
T
logan 889cce140b refactor: 弹框架构重构 + 审核规则seed API + LDAP默认配置 + 全量初始数据
弹框架构:
- Layout header/sidebar z-[200] 隔离弹框层,弹框不再遮挡面包屑
- dialog.tsx 简化: 去掉 border/backdrop-blur,统一暗色主题变量
- ModalOverlay z-[100] 对齐 Radix Dialog overlay 层级
- EditInstanceDialog 同步 z-index 修复
- 修复 InstanceGroupPage/InstancePage/LdapConfigPage 缺失 import
- 修复 InstanceGroupPage 缺失 X icon import
- RedisDetailPage ModalOverlay 迁移为 FormDialog

审核规则:
- POST /audit-rules/seed/:dbType 初始化默认规则 (mysql 11/pg 7/redis 4/mongo 4)
- POST /audit-rules/:id/toggle 开关规则
- PUT /audit-rules/:id 编辑规则
- seed.go 启动时自动创建 26 条默认审核规则

LDAP 默认配置:
- seed.go 自动写入 9 条 LDAP 配置 (8.217.205.92:389, dc=opsx,dc=vip)
- 全新 DB 启动即就位,无需手动配置
2026-05-29 22:48:45 +08:00

220 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"log"
"sql-platform/internal/model"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
)
// seedAll initializes the database with default data (idempotent).
// Safe to run on every startup — skips existing records.
func seedAll(db *gorm.DB) {
seedDefaultUsers(db)
seedDefaultWorkflow(db)
seedDefaultSettings(db)
seedLDAPConfig(db)
seedAuditRules(db)
log.Println("[seed] Default data initialized")
}
// seedDefaultUsers creates default users if they don't exist.
func seedDefaultUsers(db *gorm.DB) {
type seedUser struct {
username string
displayName string
email string
role string
password string
active bool
}
users := []seedUser{
{"admin", "超级管理员", "admin@opsx.local", "admin", "admin123", true},
{"dba", "DBA管理员", "dba@opsx.local", "dba", "admin123", true},
{"auditor", "审计员", "auditor@opsx.local", "auditor", "admin123", true},
{"developer", "开发者", "developer@opsx.local", "developer", "admin123", true},
}
for _, u := range users {
var existing model.User
if db.Where("username = ?", u.username).First(&existing).Error != nil {
hash, _ := bcrypt.GenerateFromPassword([]byte(u.password), bcrypt.DefaultCost)
email := u.email
active := u.active
user := model.User{
Username: u.username,
DisplayName: u.displayName,
Email: &email,
HashedPassword: string(hash),
Role: u.role,
IsActive: &active,
}
if err := db.Create(&user).Error; err != nil {
log.Printf("[seed] Failed to create user %s: %v", u.username, err)
} else {
log.Printf("[seed] Created user: %s (%s)", u.username, u.role)
}
}
}
}
// seedDefaultWorkflow creates a default approval workflow if none exists.
func seedDefaultWorkflow(db *gorm.DB) {
var count int64
db.Model(&model.WorkflowDefinition{}).Count(&count)
if count > 0 {
return // Already has workflows
}
// Create default workflow
t := true
wf := model.WorkflowDefinition{
Name: "默认审批流程",
Description: strPtr("系统默认审批流程:开发者提交 → DBA审核 → 执行"),
IsDefault: &t,
IsActive: &t,
}
if err := db.Create(&wf).Error; err != nil {
log.Printf("[seed] Failed to create default workflow: %v", err)
return
}
// Create steps
steps := []model.WorkflowStep{
{
WorkflowID: wf.ID,
StepNumber: 1,
Name: "提交审核",
StepType: "submit",
IsRequired: &t,
TimeoutHours: intPtr(0),
},
{
WorkflowID: wf.ID,
StepNumber: 2,
Name: "DBA审核",
StepType: "audit",
ApproverRole: strPtr("dba"),
IsRequired: &t,
TimeoutHours: intPtr(24),
},
{
WorkflowID: wf.ID,
StepNumber: 3,
Name: "执行变更",
StepType: "execute",
IsRequired: &t,
TimeoutHours: intPtr(0),
},
}
for _, s := range steps {
db.Create(&s)
}
log.Printf("[seed] Created default workflow: %s (id=%d)", wf.Name, wf.ID)
}
func intPtr(v int) *int { return &v }
// seedLDAPConfig inserts default LDAP settings if not present.
func seedLDAPConfig(db *gorm.DB) {
ldapDefaults := []model.SystemSetting{
{Key: "ldap_host", Value: "8.217.205.92", ValueType: "string", Group: "ldap", Label: "LDAP 服务器地址", Desc: strPtr("LDAP 服务器 IP 或域名"), DefaultValue: strPtr("8.217.205.92"), SortOrder: 1},
{Key: "ldap_port", Value: "389", ValueType: "number", Group: "ldap", Label: "LDAP 端口", Desc: strPtr("默认 389,LDAPS 用 636"), DefaultValue: strPtr("389"), SortOrder: 2},
{Key: "ldap_use_ssl", Value: "false", ValueType: "boolean", Group: "ldap", Label: "启用 LDAPS (SSL/TLS)", Desc: strPtr("使用 SSL/TLS 加密连接"), DefaultValue: strPtr("false"), SortOrder: 3},
{Key: "ldap_bind_dn", Value: "cn=admin,dc=opsx,dc=vip", ValueType: "string", Group: "ldap", Label: "管理员 DN", Desc: strPtr("LDAP 管理员绑定 DN"), DefaultValue: strPtr("cn=admin,dc=opsx,dc=vip"), SortOrder: 4},
{Key: "ldap_base_dn", Value: "dc=opsx,dc=vip", ValueType: "string", Group: "ldap", Label: "Base DN", Desc: strPtr("用户搜索的基础 DN"), DefaultValue: strPtr("dc=opsx,dc=vip"), SortOrder: 5},
{Key: "ldap_user_filter", Value: "(objectClass=inetOrgPerson)", ValueType: "string", Group: "ldap", Label: "用户过滤器", Desc: strPtr("LDAP 用户搜索过滤器"), DefaultValue: strPtr("(objectClass=inetOrgPerson)"), SortOrder: 6},
{Key: "ldap_uid_attr", Value: "uid", ValueType: "string", Group: "ldap", Label: "UID 属性", Desc: strPtr("用户唯一标识属性"), DefaultValue: strPtr("uid"), SortOrder: 7},
{Key: "ldap_mail_attr", Value: "mail", ValueType: "string", Group: "ldap", Label: "邮箱属性", Desc: strPtr("用户邮箱属性名"), DefaultValue: strPtr("mail"), SortOrder: 8},
{Key: "ldap_cn_attr", Value: "cn", ValueType: "string", Group: "ldap", Label: "显示名属性", Desc: strPtr("用户显示名属性名"), DefaultValue: strPtr("cn"), SortOrder: 9},
}
for _, s := range ldapDefaults {
var existing model.SystemSetting
if db.Where("`key` = ?", s.Key).First(&existing).Error != nil {
db.Create(&s)
}
}
log.Println("[seed] LDAP default config seeded")
}
// seedAuditRules creates default audit rules for all DB types.
func seedAuditRules(db *gorm.DB) {
var count int64
db.Model(&model.AuditRule{}).Count(&count)
if count > 0 {
return // Already has rules
}
enabled := true
type seedRule struct {
Name, Desc, RuleType, Severity, Pattern, Message, Value string
}
allRules := map[string][]seedRule{
"mysql": {
{"禁止 DROP TABLE", "生产环境不允许删除表", "DDL", "error", `(?i)^\s*DROP\s+TABLE`, "禁止执行 DROP TABLE 操作,可能导致数据永久丢失", "reject"},
{"禁止 DROP DATABASE", "不允许删除数据库", "DDL", "error", `(?i)^\s*DROP\s+DATABASE`, "禁止执行 DROP DATABASE 操作", "reject"},
{"禁止 TRUNCATE TABLE", "清空表数据需走审批", "DDL", "error", `(?i)^\s*TRUNCATE\s+TABLE?`, "禁止执行 TRUNCATE 操作,请使用 DELETE 逐批删除", "reject"},
{"ALTER TABLE 需审核", "表结构变更需审批", "DDL", "warning", `(?i)^\s*ALTER\s+TABLE`, "ALTER TABLE 操作需要审批后执行", "review"},
{"禁止 DELETE 无 WHERE", "删除操作必须带条件", "DML", "error", `(?i)^\s*DELETE\s+FROM?\s+\S+\s*$`, "DELETE 语句必须包含 WHERE 条件", "reject"},
{"禁止 UPDATE 无 WHERE", "更新操作必须带条件", "DML", "error", `(?i)^\s*UPDATE\s+\S+\s+SET\s+.*\s*$`, "UPDATE 语句必须包含 WHERE 条件", "reject"},
{"SELECT * 建议", "避免使用 SELECT *", "DQL", "info", `(?i)^\s*SELECT\s+\*\s+FROM`, "建议明确指定查询字段,避免使用 SELECT *", "warn"},
{"INSERT 需指定列", "INSERT 需指定列名", "DML", "warning", `(?i)^\s*INSERT\s+INTO\s+\S+\s+VALUES`, "INSERT 语句应明确指定列名", "review"},
{"禁止 GRANT ALL", "不允许授予全部权限", "DCL", "error", `(?i)^\s*GRANT\s+ALL`, "禁止使用 GRANT ALL PRIVILEGES", "reject"},
{"大表变更警告", "影响行数超过阈值需注意", "performance", "warning", "", "操作影响行数可能较大,建议在低峰期执行", "warn"},
{"禁止不带 LIMIT 的 DELETE", "批量删除建议带 LIMIT", "DML", "warning", `(?i)^\s*DELETE\s+FROM\s+\S+\s+WHERE`, "建议 DELETE 语句添加 LIMIT 子句分批删除", "warn"},
},
"postgresql": {
{"禁止 DROP TABLE", "生产环境不允许删除表", "DDL", "error", `(?i)^\s*DROP\s+TABLE`, "禁止执行 DROP TABLE 操作", "reject"},
{"禁止 DROP DATABASE", "不允许删除数据库", "DDL", "error", `(?i)^\s*DROP\s+DATABASE`, "禁止执行 DROP DATABASE 操作", "reject"},
{"禁止 DELETE 无 WHERE", "删除操作必须带条件", "DML", "error", `(?i)^\s*DELETE\s+FROM?\s+\S+\s*$`, "DELETE 语句必须包含 WHERE 条件", "reject"},
{"禁止 UPDATE 无 WHERE", "更新操作必须带条件", "DML", "error", `(?i)^\s*UPDATE\s+\S+\s+SET\s+.*\s*$`, "UPDATE 语句必须包含 WHERE 条件", "reject"},
{"ALTER TABLE 需审核", "表结构变更需审批", "DDL", "warning", `(?i)^\s*ALTER\s+TABLE`, "ALTER TABLE 操作需要审批后执行", "review"},
{"SELECT * 建议", "避免使用 SELECT *", "DQL", "info", `(?i)^\s*SELECT\s+\*\s+FROM`, "建议明确指定查询字段", "warn"},
{"禁止 GRANT ALL", "不允许授予全部权限", "DCL", "error", `(?i)^\s*GRANT\s+ALL`, "禁止使用 GRANT ALL", "reject"},
},
"redis": {
{"禁止 FLUSHALL", "不允许清空所有数据", "DDL", "error", `(?i)^FLUSHALL`, "禁止执行 FLUSHALL 操作", "reject"},
{"禁止 FLUSHDB", "清空数据库需审批", "DDL", "error", `(?i)^FLUSHDB`, "禁止执行 FLUSHDB 操作", "reject"},
{"禁止 CONFIG", "配置修改需走审批", "security", "error", `(?i)^CONFIG\s`, "CONFIG 命令需要审批后执行", "reject"},
{"KEYS 命令警告", "生产环境禁止 KEYS", "performance", "error", `(?i)^KEYS\s`, "生产环境禁止使用 KEYS 命令,请使用 SCAN", "reject"},
},
"mongodb": {
{"禁止 dropDatabase", "不允许删除数据库", "DDL", "error", `dropDatabase`, "禁止执行 dropDatabase 操作", "reject"},
{"禁止 drop 集合", "不允许删除集合", "DDL", "error", `(?i)\.drop\(\)`, "禁止执行集合 drop 操作", "reject"},
{"remove 需带条件", "删除操作必须带条件", "DML", "error", `(?i)remove\(\s*\)`, "remove 操作必须带查询条件", "reject"},
{"updateMany 需审核", "批量更新需审批", "DML", "warning", `(?i)updateMany`, "updateMany 操作需要审批后执行", "review"},
},
}
total := 0
for dbType, rules := range allRules {
for _, r := range rules {
desc := r.Desc
pattern := r.Pattern
severity := r.Severity
value := r.Value
rule := model.AuditRule{
Name: r.Name,
Desc: &desc,
RuleType: r.RuleType,
Severity: &severity,
DBType: &dbType,
Pattern: &pattern,
IsEnabled: &enabled,
Message: r.Message,
Value: &value,
}
if err := db.Create(&rule).Error; err != nil {
log.Printf("[seed] Failed to create audit rule %s/%s: %v", dbType, r.Name, err)
} else {
total++
}
}
}
log.Printf("[seed] Created %d default audit rules across 4 DB types", total)
}