This commit is contained in:
2026-08-16 20:58:57 +08:00
parent a85477be80
commit a9e1f9e6f8
42 changed files with 1569 additions and 156 deletions
+32 -20
View File
@@ -38,7 +38,7 @@ func migrateAll(db *gorm.DB) error {
// Older builds used a status label outside the administration page's supported
// state set, so normalize existing rows during migration.
func normalizeErrorRecordStatuses(db *gorm.DB) error {
return db.Model(&errorRecordPO{}).Where("status = ?", "未解决").Update("status", "未处理").Error
return db.Session(&gorm.Session{NewDB: true}).Model(&errorRecordPO{}).Where("status = ?", "未解决").Update("status", "未处理").Error
}
// migrateLegacyAuthorityAPIsToCasbinRules upgrades the early Kra join-table
@@ -46,7 +46,8 @@ func normalizeErrorRecordStatuses(db *gorm.DB) error {
// table in place for backwards compatibility, but make casbin_rule the sole
// live policy source. Existing policy rows are not duplicated.
func migrateLegacyAuthorityAPIsToCasbinRules(db *gorm.DB) error {
if !db.Migrator().HasTable(&authorityAPIPO{}) || !db.Migrator().HasTable(&casbinRulePO{}) {
clean := db.Session(&gorm.Session{NewDB: true})
if !clean.Migrator().HasTable(&authorityAPIPO{}) || !clean.Migrator().HasTable(&casbinRulePO{}) {
return nil
}
type legacyPolicy struct {
@@ -54,22 +55,28 @@ func migrateLegacyAuthorityAPIsToCasbinRules(db *gorm.DB) error {
Path string
Method string
}
var rows []legacyPolicy
if err := db.Table("sys_authority_apis sa").
query := clean.Table("sys_authority_apis sa").
Select("sa.authority_id, a.path, a.method").
Joins("JOIN sys_apis a ON a.id = sa.api_id").
Where("a.deleted_at IS NULL").Find(&rows).Error; err != nil {
Joins("JOIN sys_apis a ON a.id = sa.api_id")
// Early Kra schemas stored sys_apis without soft-delete timestamps. The
// legacy-policy migration must run before assuming that column exists;
// otherwise an upgrade from those schemas cannot start on MySQL.
if clean.Migrator().HasColumn(&apiPO{}, "deleted_at") {
query = query.Where("a.deleted_at IS NULL")
}
var rows []legacyPolicy
if err := query.Find(&rows).Error; err != nil {
return err
}
for _, row := range rows {
exists, err := policyExists(db, row.AuthorityID, row.Path, row.Method)
exists, err := policyExists(clean, row.AuthorityID, row.Path, row.Method)
if err != nil {
return err
}
if exists {
continue
}
if err := db.Create(&casbinRulePO{Ptype: "p", V0: fmt.Sprint(row.AuthorityID), V1: row.Path, V2: row.Method}).Error; err != nil {
if err := clean.Create(&casbinRulePO{Ptype: "p", V0: fmt.Sprint(row.AuthorityID), V1: row.Path, V2: row.Method}).Error; err != nil {
return err
}
}
@@ -82,7 +89,8 @@ func migrateLegacyAuthorityAPIsToCasbinRules(db *gorm.DB) error {
// composite primary key portably, so rebuild the small table once while
// preserving every existing ignore rule.
func migrateLegacyIgnoreAPITable(db *gorm.DB) error {
if !db.Migrator().HasTable(&ignoredAPIPO{}) || db.Migrator().HasColumn(&ignoredAPIPO{}, "id") {
clean := db.Session(&gorm.Session{NewDB: true})
if !clean.Migrator().HasTable(&ignoredAPIPO{}) || clean.Migrator().HasColumn(&ignoredAPIPO{}, "id") {
return nil
}
legacyTable := fmt.Sprintf("sys_ignore_apis_legacy_%d", time.Now().UnixNano())
@@ -90,7 +98,7 @@ func migrateLegacyIgnoreAPITable(db *gorm.DB) error {
Path string
Method string
}
return db.Transaction(func(tx *gorm.DB) error {
return clean.Transaction(func(tx *gorm.DB) error {
if err := tx.Migrator().RenameTable(ignoredAPIPO{}.TableName(), legacyTable); err != nil {
return fmt.Errorf("rename legacy ignore API table: %w", err)
}
@@ -123,16 +131,17 @@ func migrateLegacyIgnoreAPITable(db *gorm.DB) error {
// database has the root role but no stored API links, materialize the same
// policy set and let normal authorization read it thereafter.
func reconcileRootAuthorityAPIs(db *gorm.DB) error {
clean := db.Session(&gorm.Session{NewDB: true})
var authorityCount int64
if err := db.Model(&authorityPO{}).Where("authority_id = ?", 888).Count(&authorityCount).Error; err != nil || authorityCount == 0 {
if err := clean.Session(&gorm.Session{NewDB: true}).Model(&authorityPO{}).Where("authority_id = ?", 888).Count(&authorityCount).Error; err != nil || authorityCount == 0 {
return err
}
var policyCount int64
if err := policyScope(db).Where("v0 = ?", "888").Count(&policyCount).Error; err != nil || policyCount != 0 {
if err := policyScope(clean).Where("v0 = ?", "888").Count(&policyCount).Error; err != nil || policyCount != 0 {
return err
}
var ignored []ignoredAPIPO
if err := db.Find(&ignored).Error; err != nil {
if err := clean.Session(&gorm.Session{NewDB: true}).Find(&ignored).Error; err != nil {
return err
}
ignoreSet := make(map[string]struct{}, len(ignored))
@@ -140,7 +149,7 @@ func reconcileRootAuthorityAPIs(db *gorm.DB) error {
ignoreSet[item.Method+"\x00"+item.Path] = struct{}{}
}
var apis []apiPO
if err := db.Find(&apis).Error; err != nil {
if err := clean.Session(&gorm.Session{NewDB: true}).Find(&apis).Error; err != nil {
return err
}
rules := make([]casbinRulePO, 0, len(apis))
@@ -153,13 +162,14 @@ func reconcileRootAuthorityAPIs(db *gorm.DB) error {
if len(rules) == 0 {
return nil
}
return db.Create(&rules).Error
return clean.Session(&gorm.Session{NewDB: true}).Create(&rules).Error
}
// reconcileReferenceIndexes removes constraints created by older Kra builds
// that are not part of the administration data model. Business services own
// duplicate checks and their user-facing error messages.
func reconcileReferenceIndexes(db *gorm.DB) error {
clean := db.Session(&gorm.Session{NewDB: true})
obsolete := []struct {
model any
name string
@@ -171,8 +181,9 @@ func reconcileReferenceIndexes(db *gorm.DB) error {
{&exportTemplatePO{}, "idx_sys_export_templates_template_id"},
}
for _, item := range obsolete {
if db.Migrator().HasIndex(item.model, item.name) {
if err := db.Migrator().DropIndex(item.model, item.name); err != nil {
migrator := clean.Session(&gorm.Session{NewDB: true}).Migrator()
if migrator.HasIndex(item.model, item.name) {
if err := migrator.DropIndex(item.model, item.name); err != nil {
return fmt.Errorf("drop obsolete index %s: %w", item.name, err)
}
}
@@ -181,17 +192,18 @@ func reconcileReferenceIndexes(db *gorm.DB) error {
name string
field string
}{{"idx_sys_users_uuid", "UUID"}, {"idx_sys_users_username", "Username"}} {
unique, err := indexIsUnique(db, &userPO{}, item.name)
unique, err := indexIsUnique(clean.Session(&gorm.Session{NewDB: true}), &userPO{}, item.name)
if err != nil {
return err
}
if !unique {
continue
}
if err = db.Migrator().DropIndex(&userPO{}, item.name); err != nil {
migrator := clean.Session(&gorm.Session{NewDB: true}).Migrator()
if err = migrator.DropIndex(&userPO{}, item.name); err != nil {
return fmt.Errorf("drop legacy unique index %s: %w", item.name, err)
}
if err = db.Migrator().CreateIndex(&userPO{}, item.field); err != nil {
if err = migrator.CreateIndex(&userPO{}, item.field); err != nil {
return fmt.Errorf("create reference index %s: %w", item.name, err)
}
}