Просмотр исходного кода

refactor: improve code quality for token dynamic channel feature

1. Extract parseTokenKey helper function to eliminate code duplication
2. Add channelId validation to prevent invalid formats like sk-abc:def:ghi
3. Add database migration to drop deprecated bound_channel_id column

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat/alipay-payment
fengsilin 1 месяц назад
Родитель
Сommit
c58d01f628
2 измененных файлов: 60 добавлений и 27 удалений
  1. +27
    -27
      middleware/auth.go
  2. +33
    -0
      model/main.go

+ 27
- 27
middleware/auth.go Просмотреть файл

@@ -30,6 +30,32 @@ func validUserInfo(username string, role int) bool {
return true
}

// parseTokenKey 解析令牌字符串,提取 key 和可选的 channelId
// 支持格式:sk-{key} 或 sk-{key}:{channelId} 或 sk-{key}-{channelId}
// 返回:key, parts(parts[0]=key, parts[1]=channelId 如果指定)
func parseTokenKey(key string) (string, []string) {
key = strings.TrimPrefix(key, "sk-")

// 先按冒号分割获取 channelId(新格式:sk-{key}:{channelId})
var colonChannelId string
if colonIdx := strings.Index(key, ":"); colonIdx != -1 {
colonChannelId = key[colonIdx+1:]
key = key[:colonIdx]
}

parts := strings.Split(key, "-")
key = parts[0]

// 如果冒号方式指定了渠道,验证并追加到 parts
if colonChannelId != "" {
// 验证 channelId 为有效数字,防止 sk-abc:def:ghi 等格式
if _, err := strconv.Atoi(colonChannelId); err == nil {
parts = append(parts, colonChannelId)
}
}
return key, parts
}

func authHelper(c *gin.Context, minRole int) {
session := sessions.Default(c)
username := session.Get("username")
@@ -293,34 +319,8 @@ func TokenAuth() func(c *gin.Context) {
if strings.HasPrefix(key, "Bearer ") || strings.HasPrefix(key, "bearer ") {
key = strings.TrimSpace(key[7:])
}
key = strings.TrimPrefix(key, "sk-")
// 先按冒号分割获取 channelId(新格式:sk-{key}:{channelId})
var colonChannelId string
if colonIdx := strings.Index(key, ":"); colonIdx != -1 {
colonChannelId = key[colonIdx+1:]
key = key[:colonIdx]
}
parts = strings.Split(key, "-")
key = parts[0]
// 如果冒号方式指定了渠道,追加到 parts
if colonChannelId != "" {
parts = append(parts, colonChannelId)
}
} else {
key = strings.TrimPrefix(key, "sk-")
// 先按冒号分割获取 channelId(新格式:sk-{key}:{channelId})
var colonChannelId string
if colonIdx := strings.Index(key, ":"); colonIdx != -1 {
colonChannelId = key[colonIdx+1:]
key = key[:colonIdx]
}
parts = strings.Split(key, "-")
key = parts[0]
// 如果冒号方式指定了渠道,追加到 parts
if colonChannelId != "" {
parts = append(parts, colonChannelId)
}
}
key, parts = parseTokenKey(key)
token, err := model.ValidateUserToken(key)
if token != nil {
id := c.GetInt("id")


+ 33
- 0
model/main.go Просмотреть файл

@@ -250,6 +250,8 @@ func InitLogDB() (err error) {
func migrateDB() error {
// Migrate price_amount column from float/double to decimal for existing tables
migrateSubscriptionPlanPriceAmount()
// Drop bound_channel_id column from tokens table (deprecated field)
migrateTokenDropBoundChannelId()

err := DB.AutoMigrate(
&Channel{},
@@ -295,6 +297,8 @@ func migrateDB() error {
}

func migrateDBFast() error {
// Drop bound_channel_id column from tokens table (deprecated field)
migrateTokenDropBoundChannelId()

var wg sync.WaitGroup

@@ -506,6 +510,35 @@ func migrateSubscriptionPlanPriceAmount() {
}
}

// migrateTokenDropBoundChannelId 删除 tokens 表中的 bound_channel_id 列(已废弃的字段)
func migrateTokenDropBoundChannelId() {
// SQLite 不支持 DROP COLUMN,跳过迁移
if common.UsingSQLite {
return
}

tableName := "tokens"
columnName := "bound_channel_id"

// 检查表是否存在
if !DB.Migrator().HasTable(tableName) {
return
}

// 检查列是否存在
if !DB.Migrator().HasColumn(&Token{}, columnName) {
return
}

// MySQL 和 PostgreSQL 都支持此语法
dropSQL := fmt.Sprintf("ALTER TABLE %s DROP COLUMN %s", tableName, columnName)
if err := DB.Exec(dropSQL).Error; err != nil {
common.SysLog(fmt.Sprintf("Warning: failed to drop %s.%s: %v", tableName, columnName, err))
} else {
common.SysLog(fmt.Sprintf("Successfully dropped deprecated column %s.%s", tableName, columnName))
}
}

func closeDB(db *gorm.DB) error {
sqlDB, err := db.DB()
if err != nil {


Загрузка…
Отмена
Сохранить