fix: align admin behavior and Kratos integrations

This commit is contained in:
2026-08-14 14:14:51 +08:00
parent 8c4c6ba661
commit e0b0e1d8c8
123 changed files with 4334 additions and 1877 deletions
+78 -35
View File
@@ -16,21 +16,71 @@ import (
var ProviderSet = wire.NewSet(NewData, NewSystemRepo, NewAccessRepo, NewMenuRepo, NewOrganizationRepo, NewSettingsRepo, NewVersionRepo, NewExportRepo, NewAuditRepo, NewTaskRepo, NewMediaRepo, NewAnnouncementRepo, NewEmailRepo, NewCache, NewFileStorage)
type Data struct {
mu sync.RWMutex
initMu sync.Mutex
gormDB *gorm.DB
redis *redis.Client
database *conf.Data_Database
config *conf.Data
admin *conf.AdminBackend
configMu sync.Mutex
gormDB *reloadableDB
redis *reloadableRedis
runtime *conf.Runtime
storage *reloadableStorage
dbListMu sync.RWMutex
dbList map[string]*gorm.DB
}
func NewData(c *conf.Data, admin *conf.AdminBackend) (*Data, func(), error) {
func openDatabaseList(configs []*conf.Data_Database) (map[string]*gorm.DB, error) {
items := make(map[string]*gorm.DB)
for _, config := range configs {
if config == nil || config.Disable || config.AliasName == "" {
continue
}
db, err := openDatabase(config, false, "")
if err != nil {
for _, opened := range items {
if sqlDB, dbErr := opened.DB(); dbErr == nil {
_ = sqlDB.Close()
}
}
return nil, fmt.Errorf("open database %q: %w", config.AliasName, err)
}
items[config.AliasName] = db
}
return items, nil
}
func closeDatabaseList(items map[string]*gorm.DB) {
for _, db := range items {
if sqlDB, err := db.DB(); err == nil {
_ = sqlDB.Close()
}
}
}
func (d *Data) replaceDatabaseList(items map[string]*gorm.DB) {
d.dbListMu.Lock()
old := d.dbList
d.dbList = items
d.dbListMu.Unlock()
closeDatabaseList(old)
}
func (d *Data) database(name string) (*gorm.DB, error) {
if name == "" {
return d.gormDB.DB(), nil
}
d.dbListMu.RLock()
db := d.dbList[name]
d.dbListMu.RUnlock()
if db == nil {
return nil, fmt.Errorf("database %q not found", name)
}
return db, nil
}
func NewData(runtime *conf.Runtime) (*Data, func(), error) {
c := runtime.Data()
if c == nil || c.Database == nil {
return nil, nil, fmt.Errorf("database configuration is required")
}
d := &Data{database: c.Database, config: c, admin: admin}
d := &Data{runtime: runtime}
db, err := openDatabase(c.Database, false, "")
usingFallback := false
if err != nil {
@@ -43,31 +93,33 @@ func NewData(c *conf.Data, admin *conf.AdminBackend) (*Data, func(), error) {
}
usingFallback = true
}
d.gormDB = db
if !usingFallback {
d.gormDB = newReloadableDB(db)
d.dbList, err = openDatabaseList(c.DatabaseList)
if err != nil {
d.gormDB.close()
return nil, nil, err
}
admin := runtime.Admin()
disableAutoMigrate := admin != nil && admin.System != nil && admin.System.DisableAutoMigrate
if !usingFallback && !disableAutoMigrate {
if err = migrateAll(db); err != nil {
return nil, nil, fmt.Errorf("migrate tables: %w", err)
}
}
if candidate := openRedis(c.Redis); candidate != nil {
d.redis = candidate
}
useRedis := admin != nil && admin.System != nil && admin.System.UseRedis
d.redis = newReloadableRedis(openRedis(c.Redis, useRedis))
stopConfigWatcher := d.watchConfig()
cleanup := func() {
d.mu.RLock()
db := d.gormDB
d.mu.RUnlock()
if sqlDB, closeErr := db.DB(); closeErr == nil {
_ = sqlDB.Close()
}
if d.redis != nil {
_ = d.redis.Close()
}
stopConfigWatcher()
d.gormDB.close()
closeDatabaseList(d.dbList)
d.redis.close()
}
return d, cleanup, nil
}
func openRedis(config *conf.Data_Redis) *redis.Client {
if config == nil || config.Addr == "" {
func openRedis(config *conf.Data_Redis, enabled bool) *redis.Client {
if !enabled || config == nil || config.Addr == "" {
return nil
}
options := &redis.Options{Addr: config.Addr, Network: config.Network}
@@ -89,15 +141,6 @@ func openRedis(config *conf.Data_Redis) *redis.Client {
}
func (d *Data) activateDatabase(db *gorm.DB, config *conf.Data_Database) {
d.mu.Lock()
old := d.gormDB
d.gormDB = db
d.database = config
d.config.Database = config
d.mu.Unlock()
if old != nil {
if sqlDB, e := old.DB(); e == nil {
_ = sqlDB.Close()
}
}
d.gormDB.replace(db)
d.runtime.UpdateDatabase(config)
}