You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/internal/model/migrate.go

269 lines
6.1 KiB
Go

2 weeks ago
package model
import (
"fmt"
"github.com/veypi/vbase/internal/config"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var DB *gorm.DB
// InitDB 初始化数据库
func InitDB() error {
cfg := config.C.Database
var dialector gorm.Dialector
switch cfg.Type {
case "mysql":
dialector = mysql.Open(cfg.DSN)
case "postgres":
dialector = postgres.Open(cfg.DSN)
case "sqlite":
dialector = sqlite.Open(cfg.DSN)
default:
return fmt.Errorf("unsupported database type: %s", cfg.Type)
}
var err error
DB, err = gorm.Open(dialector, &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
return fmt.Errorf("failed to connect database: %w", err)
}
sqlDB, err := DB.DB()
if err != nil {
return err
}
sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
sqlDB.SetConnMaxLifetime(cfg.ConnMaxLifetime)
return nil
}
// AutoMigrate 自动迁移表结构
func AutoMigrate() error {
return DB.AutoMigrate(
&User{},
&Identity{},
&Session{},
&Org{},
&OrgMember{},
&Policy{},
&Role{},
&OAuthClient{},
&OAuthAuthorization{},
&OAuthToken{},
)
}
// InitSystemData 初始化系统数据
func InitSystemData() error {
return DB.Transaction(func(tx *gorm.DB) error {
// 1. 创建系统策略
if err := initSystemPolicies(tx); err != nil {
return err
}
// 2. 检查是否需要创建初始管理员
var count int64
if err := tx.Model(&User{}).Count(&count).Error; err != nil {
return err
}
if count == 0 {
if err := initAdminUser(tx); err != nil {
return err
}
}
return nil
})
}
// initSystemPolicies 创建系统内置策略
func initSystemPolicies(tx *gorm.DB) error {
policies := []Policy{
{
Code: SysPolicyUserReadOwn,
Name: "读取自己",
Description: "用户读取自己的信息",
Resource: "user",
Action: "read",
Condition: "resource.id == user.id",
Effect: EffectAllow,
IsSystem: true,
},
{
Code: SysPolicyUserUpdateOwn,
Name: "更新自己",
Description: "用户更新自己的信息",
Resource: "user",
Action: "update",
Condition: "resource.id == user.id",
Effect: EffectAllow,
IsSystem: true,
},
{
Code: SysPolicyUserDeleteOwn,
Name: "删除自己",
Description: "用户删除自己的账号",
Resource: "user",
Action: "delete",
Condition: "resource.id == user.id",
Effect: EffectAllow,
IsSystem: true,
},
{
Code: SysPolicyOrgAdmin,
Name: "组织管理员",
Description: "组织所有者拥有所有权限",
Resource: "*",
Action: "*",
Condition: "org.owner_id == user.id",
Effect: EffectAllow,
Priority: 100,
IsSystem: true,
},
{
Code: SysPolicyOrgRead,
Name: "读取组织",
Description: "组织成员可读组织信息",
Resource: "org",
Action: "read",
Condition: "member.org_id == org.id",
Effect: EffectAllow,
IsSystem: true,
},
{
Code: SysPolicyMemberRead,
Name: "读取成员",
Description: "读取组织成员列表",
Resource: "member",
Action: "read",
Condition: "member.org_id == org.id",
Effect: EffectAllow,
IsSystem: true,
},
{
Code: SysPolicyMemberManage,
Name: "管理成员",
Description: "管理组织成员",
Resource: "member",
Action: "*",
Condition: "",
Effect: EffectAllow,
IsSystem: true,
},
{
Code: SysPolicyRoleRead,
Name: "读取角色",
Description: "读取组织角色",
Resource: "role",
Action: "read",
Condition: "resource.org_id == org.id",
Effect: EffectAllow,
IsSystem: true,
},
{
Code: SysPolicyRoleManage,
Name: "管理角色",
Description: "管理组织角色",
Resource: "role",
Action: "*",
Condition: "",
Effect: EffectAllow,
IsSystem: true,
},
{
Code: SysPolicyPolicyRead,
Name: "读取策略",
Description: "读取策略",
Resource: "policy",
Action: "read",
Condition: "",
Effect: EffectAllow,
IsSystem: true,
},
{
Code: SysPolicyPolicyManage,
Name: "管理策略",
Description: "管理策略",
Resource: "policy",
Action: "*",
Condition: "",
Effect: EffectAllow,
IsSystem: true,
},
}
for _, p := range policies {
var existing Policy
if err := tx.Where("code = ?", p.Code).First(&existing).Error; err != nil {
if err == gorm.ErrRecordNotFound {
if err := tx.Create(&p).Error; err != nil {
return err
}
} else {
return err
}
}
}
return nil
}
// initAdminUser 创建初始管理员
func initAdminUser(tx *gorm.DB) error {
adminCfg := config.C.App.InitAdmin
// 生成随机密码(如果未配置)
password := adminCfg.Password
if password == "" {
password = generateRandomPassword(16)
fmt.Printf("\n========================================\n")
fmt.Printf("Initial admin user created!\n")
fmt.Printf("Username: %s\n", adminCfg.Username)
fmt.Printf("Password: %s\n", password)
fmt.Printf("Email: %s\n", adminCfg.Email)
fmt.Printf("========================================\n\n")
}
// 密码哈希这里需要crypto包后续实现
// hashedPassword, _ := crypto.HashPassword(password)
admin := &User{
Username: adminCfg.Username,
Password: password, // TODO: hash password
Email: adminCfg.Email,
Nickname: "Administrator",
Status: UserStatusActive,
EmailVerified: true,
PhoneVerified: false,
}
if err := tx.Create(admin).Error; err != nil {
return err
}
return nil
}
// generateRandomPassword 生成随机密码
func generateRandomPassword(length int) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
result := make([]byte, length)
for i := range result {
result[i] = charset[i%len(charset)]
}
return string(result)
}