diff --git a/cmd/common.go b/cmd/common.go index 8a73f9b0..d88a86eb 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -1,6 +1,7 @@ package cmd import ( + "github.com/alist-org/alist/v3/internal/bootstrap/patch/v3_46_0" "os" "path/filepath" "strconv" @@ -16,6 +17,12 @@ func Init() { bootstrap.InitConfig() bootstrap.Log() bootstrap.InitDB() + + if v3_46_0.IsLegacyRoleDetected() { + utils.Log.Warnf("Detected legacy role format, executing ConvertLegacyRoles patch early...") + v3_46_0.ConvertLegacyRoles() + } + data.InitData() bootstrap.InitStreamLimit() bootstrap.InitIndex() diff --git a/internal/bootstrap/patch/v3_46_0/convert_role.go b/internal/bootstrap/patch/v3_46_0/convert_role.go index 43799485..3aac95b6 100644 --- a/internal/bootstrap/patch/v3_46_0/convert_role.go +++ b/internal/bootstrap/patch/v3_46_0/convert_role.go @@ -1,7 +1,10 @@ package v3_46_0 import ( + "database/sql" + "encoding/json" "errors" + "github.com/alist-org/alist/v3/internal/conf" "github.com/alist-org/alist/v3/internal/db" "github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/op" @@ -83,47 +86,101 @@ func ConvertLegacyRoles() { } } - users, _, err := op.GetUsers(1, -1) + rawDb := db.GetDb() + table := conf.Conf.Database.TablePrefix + "users" + rows, err := rawDb.Table(table).Select("id, username, role").Rows() if err != nil { utils.Log.Errorf("[convert roles] failed to get users: %v", err) return } + defer rows.Close() - for i := range users { - user := users[i] - if user.Role == nil { + var updatedCount int + for rows.Next() { + var id uint + var username string + var rawRole []byte + + if err := rows.Scan(&id, &username, &rawRole); err != nil { + utils.Log.Warnf("[convert roles] skip user scan err: %v", err) continue } - changed := false - var roles model.Roles - for _, r := range user.Role { + + utils.Log.Debugf("[convert roles] user: %s raw role: %s", username, string(rawRole)) + + if len(rawRole) == 0 { + continue + } + + var oldRoles []int + wasSingleInt := false + if err := json.Unmarshal(rawRole, &oldRoles); err != nil { + var single int + if err := json.Unmarshal(rawRole, &single); err != nil { + utils.Log.Warnf("[convert roles] user %s has invalid role: %s", username, string(rawRole)) + continue + } + oldRoles = []int{single} + wasSingleInt = true + } + + var newRoles model.Roles + for _, r := range oldRoles { switch r { case model.ADMIN: - roles = append(roles, int(adminRole.ID)) - if int(adminRole.ID) != r { - changed = true - } + newRoles = append(newRoles, int(adminRole.ID)) case model.GUEST: - roles = append(roles, int(guestRole.ID)) - if int(guestRole.ID) != r { - changed = true - } + newRoles = append(newRoles, int(guestRole.ID)) case model.GENERAL: - roles = append(roles, int(generalRole.ID)) - if int(generalRole.ID) != r { - changed = true - } + newRoles = append(newRoles, int(generalRole.ID)) default: - roles = append(roles, r) + newRoles = append(newRoles, r) } } - if changed { - user.Role = roles - if err := db.UpdateUser(&user); err != nil { - utils.Log.Errorf("[convert roles] failed to update user %s: %v", user.Username, err) + + if wasSingleInt { + err := rawDb.Table(table).Where("id = ?", id).Update("role", newRoles).Error + if err != nil { + utils.Log.Errorf("[convert roles] failed to update user %s: %v", username, err) + } else { + updatedCount++ + utils.Log.Infof("[convert roles] updated user %s: %v → %v", username, oldRoles, newRoles) } } } - utils.Log.Infof("[convert roles] completed role conversion for %d users", len(users)) + utils.Log.Infof("[convert roles] completed role conversion for %d users", updatedCount) +} + +func IsLegacyRoleDetected() bool { + rawDb := db.GetDb() + table := conf.Conf.Database.TablePrefix + "users" + rows, err := rawDb.Table(table).Select("role").Rows() + if err != nil { + utils.Log.Errorf("[role check] failed to scan user roles: %v", err) + return false + } + defer rows.Close() + + for rows.Next() { + var raw sql.RawBytes + if err := rows.Scan(&raw); err != nil { + continue + } + if len(raw) == 0 { + continue + } + + var roles []int + if err := json.Unmarshal(raw, &roles); err == nil { + continue + } + + var single int + if err := json.Unmarshal(raw, &single); err == nil { + utils.Log.Infof("[role check] detected legacy int role: %d", single) + return true + } + } + return false }