1. 菜单路由修复: /roles /users /logs-audit /terminal-audit 等系统管理子菜单 通过 useLocation 同步 URL 路径到 Tab 选中状态 2. WebSSH错误提示优化: Go error → 友好中文提示 i/o timeout → 连接超时(防火墙或网络不通) connection refused → 端口未开放/SSH服务未启动 cannot find session key → 密码或密钥认证失败 连接提示增加端口号显示 3. 角色权限展示: 31项权限种子数据 (资产管理/云资源/K8s/发布/监控/AIOps/系统管理) 4个角色权限分配: admin(31) ops(27) developer(16) viewer(3) 前端角色卡片显示权限名称而非原始code 4. 操作审计修复: 删除/创建/更新服务器均记录审计日志 批量删除逐条记录审计 5. WebSSH终端审计 (JumpServer模式): - 新增 TerminalSession 模型记录会话(用户/服务器/时间/状态) - webshell.go 录制终端输出(1MB上限) - 终端审计页面: 会话列表+状态筛选+批量删除 - 操作回放: xterm.js 重放录制的终端输出 - WS连接增加JWT鉴权+token传递
224 lines
9.7 KiB
Go
224 lines
9.7 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"ezops/internal/models"
|
|
|
|
"go.uber.org/zap"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// seedData 初始化种子数据(admin用户 + LDAP配置 + 审批模板 + 权限角色)
|
|
func seedData(db *gorm.DB) {
|
|
// 1. 创建默认 admin 用户(仅首次)
|
|
var userCount int64
|
|
db.Model(&models.User{}).Count(&userCount)
|
|
if userCount == 0 {
|
|
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
|
|
admin := models.User{
|
|
Username: "admin",
|
|
Password: string(hashedPassword),
|
|
Email: "admin@ezops.local",
|
|
RealName: "管理员",
|
|
Status: 1,
|
|
IsAdmin: true,
|
|
AuthSource: "local",
|
|
}
|
|
if err := db.Create(&admin).Error; err != nil {
|
|
zap.L().Warn("failed to seed admin user", zap.Error(err))
|
|
} else {
|
|
zap.L().Info("seeded default admin user (admin/123456)")
|
|
}
|
|
}
|
|
|
|
// 2. 创建默认 LDAP 配置
|
|
var ldapCount int64
|
|
db.Model(&models.LDAPConfig{}).Count(&ldapCount)
|
|
if ldapCount == 0 {
|
|
port := 389
|
|
useLDAPS := false
|
|
searchFilter := "(objectClass=inetOrgPerson)"
|
|
userMapping := `{"username":"uid","email":"mail","display_name":"cn"}`
|
|
ldapCfg := models.LDAPConfig{
|
|
LDAPHost: "8.217.205.92",
|
|
LDAPPort: &port,
|
|
UseLDAPS: &useLDAPS,
|
|
AdminDN: "cn=admin,dc=opsx,dc=vip",
|
|
AdminPassword: "LQl2GX7EEg2N",
|
|
BaseDN: "dc=opsx,dc=vip",
|
|
SearchFilter: &searchFilter,
|
|
UserMapping: &userMapping,
|
|
}
|
|
if err := db.Create(&ldapCfg).Error; err != nil {
|
|
zap.L().Warn("failed to seed LDAP config", zap.Error(err))
|
|
} else {
|
|
zap.L().Info("seeded default LDAP config (8.217.205.92:389)")
|
|
}
|
|
}
|
|
|
|
// 3. 创建默认审批模板(资源申请)
|
|
var tplCount int64
|
|
db.Model(&models.ApprovalTemplate{}).Count(&tplCount)
|
|
if tplCount == 0 {
|
|
templates := []models.ApprovalTemplate{
|
|
{
|
|
Name: "云服务器申请",
|
|
Code: "server_apply",
|
|
Description: "申请云服务器资源(ECS/EC2),包含规格、数量、用途等信息",
|
|
Type: "server_apply",
|
|
FormSchema: `{
|
|
"fields": [
|
|
{"key": "provider", "label": "云厂商", "type": "select", "options": ["阿里云","腾讯云","华为云","AWS"], "required": true},
|
|
{"key": "region", "label": "地域", "type": "text", "placeholder": "如 cn-hangzhou", "required": true},
|
|
{"key": "spec", "label": "规格", "type": "select", "options": ["2C4G","4C8G","8C16G","16C32G","32C64G"], "required": true},
|
|
{"key": "disk", "label": "系统盘(GB)", "type": "number", "default": 40, "required": true},
|
|
{"key": "count", "label": "数量(台)", "type": "number", "default": 1, "required": true},
|
|
{"key": "duration", "label": "时长", "type": "select", "options": ["1个月","3个月","6个月","1年"], "required": true},
|
|
{"key": "purpose", "label": "用途说明", "type": "textarea", "required": true}
|
|
]
|
|
}`,
|
|
FlowConfig: `[{"name":"直接主管","approver_role":"team_lead"},{"name":"运维负责人","approver_role":"ops_manager"}]`,
|
|
Enabled: true,
|
|
},
|
|
{
|
|
Name: "应用部署申请",
|
|
Code: "app_deploy",
|
|
Description: "申请新应用部署,包含 Git 地址、端口、Nacos 等配置信息",
|
|
Type: "app_deploy",
|
|
FormSchema: `{
|
|
"fields": [
|
|
{"key": "app_name", "label": "应用名称", "type": "text", "placeholder": "如 user-service", "required": true},
|
|
{"key": "git_url", "label": "Git 地址", "type": "text", "placeholder": "如 https://github.com/org/repo.git", "required": true},
|
|
{"key": "branch", "label": "分支", "type": "text", "default": "main", "required": true},
|
|
{"key": "language", "label": "开发语言", "type": "select", "options": ["Java","Go","Python","Node.js","C++"], "required": true},
|
|
{"key": "http_port", "label": "HTTP 端口", "type": "number", "placeholder": "如 8080", "required": false},
|
|
{"key": "grpc_port", "label": "gRPC 端口", "type": "number", "placeholder": "如 9090", "required": false},
|
|
{"key": "nacos_addr", "label": "Nacos 地址", "type": "text", "placeholder": "如 nacos.opsx.vip:8848", "required": true},
|
|
{"key": "nacos_namespace", "label": "Nacos 命名空间", "type": "text", "placeholder": "如 production", "required": false},
|
|
{"key": "env", "label": "部署环境", "type": "select", "options": ["dev","staging","prod"], "required": true},
|
|
{"key": "replicas", "label": "副本数", "type": "number", "default": 2, "required": true},
|
|
{"key": "remark", "label": "备注", "type": "textarea", "required": false}
|
|
]
|
|
}`,
|
|
FlowConfig: `[{"name":"技术负责人","approver_role":"tech_lead"},{"name":"运维负责人","approver_role":"ops_manager"}]`,
|
|
Enabled: true,
|
|
},
|
|
}
|
|
if err := db.Create(&templates).Error; err != nil {
|
|
zap.L().Warn("failed to seed approval templates", zap.Error(err))
|
|
} else {
|
|
zap.L().Info("seeded default approval templates (server_apply + app_deploy)")
|
|
}
|
|
}
|
|
|
|
// 4. 创建默认权限和角色权限映射
|
|
var permCount int64
|
|
db.Model(&models.Permission{}).Count(&permCount)
|
|
if permCount == 0 {
|
|
perms := []models.Permission{
|
|
// 资产管理
|
|
{Name: "服务器管理", Code: "server:view", Type: "menu", Sort: 1, Status: 1},
|
|
{Name: "服务器创建", Code: "server:create", Type: "button", Sort: 2, Status: 1},
|
|
{Name: "服务器编辑", Code: "server:update", Type: "button", Sort: 3, Status: 1},
|
|
{Name: "服务器删除", Code: "server:delete", Type: "button", Sort: 4, Status: 1},
|
|
{Name: "WebShell终端", Code: "server:webshell", Type: "button", Sort: 5, Status: 1},
|
|
// 云资源
|
|
{Name: "云账号管理", Code: "cloud:account", Type: "menu", Sort: 10, Status: 1},
|
|
{Name: "域名管理", Code: "cloud:domain", Type: "menu", Sort: 11, Status: 1},
|
|
{Name: "云账单", Code: "cloud:billing", Type: "menu", Sort: 12, Status: 1},
|
|
// Kubernetes
|
|
{Name: "集群管理", Code: "k8s:cluster", Type: "menu", Sort: 20, Status: 1},
|
|
{Name: "节点管理", Code: "k8s:nodes", Type: "menu", Sort: 21, Status: 1},
|
|
{Name: "工作负载", Code: "k8s:workloads", Type: "menu", Sort: 22, Status: 1},
|
|
{Name: "架构拓扑", Code: "k8s:topology", Type: "menu", Sort: 23, Status: 1},
|
|
// 应用发布
|
|
{Name: "流水线", Code: "deploy:pipeline", Type: "menu", Sort: 30, Status: 1},
|
|
{Name: "应用发布", Code: "deploy:release", Type: "menu", Sort: 31, Status: 1},
|
|
// 监控告警
|
|
{Name: "告警中心", Code: "monitor:alert", Type: "menu", Sort: 40, Status: 1},
|
|
{Name: "告警规则", Code: "monitor:rule", Type: "button", Sort: 41, Status: 1},
|
|
{Name: "Agent监控", Code: "agent:monitor", Type: "menu", Sort: 42, Status: 1},
|
|
{Name: "Agent告警", Code: "agent:alert", Type: "menu", Sort: 43, Status: 1},
|
|
// 自动化运维
|
|
{Name: "定时任务", Code: "auto:cron", Type: "menu", Sort: 50, Status: 1},
|
|
{Name: "脚本管理", Code: "auto:script", Type: "menu", Sort: 51, Status: 1},
|
|
{Name: "Ansible", Code: "auto:ansible", Type: "menu", Sort: 52, Status: 1},
|
|
// AIOps
|
|
{Name: "智能助手", Code: "aiops:assistant", Type: "menu", Sort: 60, Status: 1},
|
|
{Name: "告警分析", Code: "aiops:analysis", Type: "menu", Sort: 61, Status: 1},
|
|
{Name: "容量预测", Code: "aiops:forecast", Type: "menu", Sort: 62, Status: 1},
|
|
// 资源申请
|
|
{Name: "资源申请", Code: "approval:apply", Type: "menu", Sort: 70, Status: 1},
|
|
{Name: "审批管理", Code: "approval:manage", Type: "button", Sort: 71, Status: 1},
|
|
// 系统管理
|
|
{Name: "用户管理", Code: "system:user", Type: "menu", Sort: 80, Status: 1},
|
|
{Name: "角色权限", Code: "system:role", Type: "menu", Sort: 81, Status: 1},
|
|
{Name: "操作审计", Code: "system:audit", Type: "menu", Sort: 82, Status: 1},
|
|
{Name: "LDAP配置", Code: "system:ldap", Type: "menu", Sort: 83, Status: 1},
|
|
// 仪表盘
|
|
{Name: "仪表盘", Code: "dashboard:view", Type: "menu", Sort: 90, Status: 1},
|
|
}
|
|
db.Create(&perms)
|
|
zap.L().Info("seeded default permissions")
|
|
}
|
|
|
|
// 5. 创建默认角色(如果不存在)
|
|
var roleCount int64
|
|
db.Model(&models.Role{}).Count(&roleCount)
|
|
if roleCount == 0 {
|
|
roles := []models.Role{
|
|
{Name: "管理员", Code: "admin", Description: "系统管理员,拥有全部权限", Status: 1},
|
|
{Name: "运维工程师", Code: "ops", Description: "日常运维管理", Status: 1},
|
|
{Name: "开发工程师", Code: "developer", Description: "开发人员,只读权限", Status: 1},
|
|
{Name: "只读用户", Code: "viewer", Description: "仅查看权限", Status: 1},
|
|
}
|
|
db.Create(&roles)
|
|
zap.L().Info("seeded default roles")
|
|
|
|
// 为各角色分配权限
|
|
var allPerms []models.Permission
|
|
db.Find(&allPerms)
|
|
for _, role := range roles {
|
|
var permIds []uint
|
|
switch role.Code {
|
|
case "admin":
|
|
for _, p := range allPerms {
|
|
permIds = append(permIds, p.ID)
|
|
}
|
|
case "ops":
|
|
for _, p := range allPerms {
|
|
if !strings.HasPrefix(p.Code, "system:") {
|
|
permIds = append(permIds, p.ID)
|
|
}
|
|
}
|
|
case "developer":
|
|
devPerms := map[string]bool{
|
|
"dashboard:view": true, "server:view": true, "cloud:account": true,
|
|
"cloud:domain": true, "cloud:billing": true,
|
|
"k8s:cluster": true, "k8s:nodes": true, "k8s:workloads": true, "k8s:topology": true,
|
|
"monitor:alert": true, "deploy:pipeline": true, "deploy:release": true,
|
|
"approval:apply": true, "aiops:assistant": true, "aiops:analysis": true, "aiops:forecast": true,
|
|
}
|
|
for _, p := range allPerms {
|
|
if devPerms[p.Code] {
|
|
permIds = append(permIds, p.ID)
|
|
}
|
|
}
|
|
case "viewer":
|
|
for _, p := range allPerms {
|
|
if p.Code == "dashboard:view" || p.Code == "server:view" || p.Code == "monitor:alert" {
|
|
permIds = append(permIds, p.ID)
|
|
}
|
|
}
|
|
}
|
|
if len(permIds) > 0 {
|
|
var rolePerms []models.Permission
|
|
db.Where("id IN ?", permIds).Find(&rolePerms)
|
|
db.Model(&role).Association("Permissions").Append(rolePerms)
|
|
}
|
|
}
|
|
}
|
|
}
|