refactor(convert_role): Improve role conversion logic for legacy formats (#9219)

- Add new imports: `database/sql`, `encoding/json`, and `conf` package in `convert_role.go`.
- Simplify permission entry initialization by removing redundant struct formatting.
- Update error logging messages for better clarity.
- Replace `op.GetUsers` with direct database access for fetching user roles.
- Implement role update logic using `rawDb` and handle legacy int role conversion.
- Count the number of users whose roles are updated and log completion.
- Introduce `IsLegacyRoleDetected` function to check for legacy role formats.
- Modify `cmd/common.go` to invoke role conversion if legacy format is detected.
pull/9228/head v3.46.1
千石 2025-07-26 15:20:08 +08:00 committed by GitHub
parent 00120cba27
commit f61d13d433
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 89 additions and 25 deletions

View File

@ -1,6 +1,7 @@
package cmd package cmd
import ( import (
"github.com/alist-org/alist/v3/internal/bootstrap/patch/v3_46_0"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -16,6 +17,12 @@ func Init() {
bootstrap.InitConfig() bootstrap.InitConfig()
bootstrap.Log() bootstrap.Log()
bootstrap.InitDB() bootstrap.InitDB()
if v3_46_0.IsLegacyRoleDetected() {
utils.Log.Warnf("Detected legacy role format, executing ConvertLegacyRoles patch early...")
v3_46_0.ConvertLegacyRoles()
}
data.InitData() data.InitData()
bootstrap.InitStreamLimit() bootstrap.InitStreamLimit()
bootstrap.InitIndex() bootstrap.InitIndex()

View File

@ -1,7 +1,10 @@
package v3_46_0 package v3_46_0
import ( import (
"database/sql"
"encoding/json"
"errors" "errors"
"github.com/alist-org/alist/v3/internal/conf"
"github.com/alist-org/alist/v3/internal/db" "github.com/alist-org/alist/v3/internal/db"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/op" "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 { if err != nil {
utils.Log.Errorf("[convert roles] failed to get users: %v", err) utils.Log.Errorf("[convert roles] failed to get users: %v", err)
return return
} }
defer rows.Close()
for i := range users { var updatedCount int
user := users[i] for rows.Next() {
if user.Role == nil { 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 continue
} }
changed := false
var roles model.Roles utils.Log.Debugf("[convert roles] user: %s raw role: %s", username, string(rawRole))
for _, r := range user.Role {
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 { switch r {
case model.ADMIN: case model.ADMIN:
roles = append(roles, int(adminRole.ID)) newRoles = append(newRoles, int(adminRole.ID))
if int(adminRole.ID) != r {
changed = true
}
case model.GUEST: case model.GUEST:
roles = append(roles, int(guestRole.ID)) newRoles = append(newRoles, int(guestRole.ID))
if int(guestRole.ID) != r {
changed = true
}
case model.GENERAL: case model.GENERAL:
roles = append(roles, int(generalRole.ID)) newRoles = append(newRoles, int(generalRole.ID))
if int(generalRole.ID) != r {
changed = true
}
default: default:
roles = append(roles, r) newRoles = append(newRoles, r)
} }
} }
if changed {
user.Role = roles if wasSingleInt {
if err := db.UpdateUser(&user); err != nil { err := rawDb.Table(table).Where("id = ?", id).Update("role", newRoles).Error
utils.Log.Errorf("[convert roles] failed to update user %s: %v", user.Username, err) 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
} }