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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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)
}