mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-03-29 23:10:35 +00:00
The i18n middleware runs before UserAuth, so user settings weren't available when language was detected. Now GetLangFromContext checks user settings first (set by UserAuth) before falling back to the language set by middleware or Accept-Language header.
234 lines
5.8 KiB
Go
234 lines
5.8 KiB
Go
package model
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/constant"
|
|
"github.com/QuantumNous/new-api/dto"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/bytedance/gopkg/util/gopool"
|
|
)
|
|
|
|
// UserBase struct remains the same as it represents the cached data structure
|
|
type UserBase struct {
|
|
Id int `json:"id"`
|
|
Group string `json:"group"`
|
|
Email string `json:"email"`
|
|
Quota int `json:"quota"`
|
|
Status int `json:"status"`
|
|
Username string `json:"username"`
|
|
Setting string `json:"setting"`
|
|
}
|
|
|
|
func (user *UserBase) WriteContext(c *gin.Context) {
|
|
common.SetContextKey(c, constant.ContextKeyUserGroup, user.Group)
|
|
common.SetContextKey(c, constant.ContextKeyUserQuota, user.Quota)
|
|
common.SetContextKey(c, constant.ContextKeyUserStatus, user.Status)
|
|
common.SetContextKey(c, constant.ContextKeyUserEmail, user.Email)
|
|
common.SetContextKey(c, constant.ContextKeyUserName, user.Username)
|
|
common.SetContextKey(c, constant.ContextKeyUserSetting, user.GetSetting())
|
|
}
|
|
|
|
func (user *UserBase) GetSetting() dto.UserSetting {
|
|
setting := dto.UserSetting{}
|
|
if user.Setting != "" {
|
|
err := common.Unmarshal([]byte(user.Setting), &setting)
|
|
if err != nil {
|
|
common.SysLog("failed to unmarshal setting: " + err.Error())
|
|
}
|
|
}
|
|
return setting
|
|
}
|
|
|
|
// getUserCacheKey returns the key for user cache
|
|
func getUserCacheKey(userId int) string {
|
|
return fmt.Sprintf("user:%d", userId)
|
|
}
|
|
|
|
// invalidateUserCache clears user cache
|
|
func invalidateUserCache(userId int) error {
|
|
if !common.RedisEnabled {
|
|
return nil
|
|
}
|
|
return common.RedisDelKey(getUserCacheKey(userId))
|
|
}
|
|
|
|
// updateUserCache updates all user cache fields using hash
|
|
func updateUserCache(user User) error {
|
|
if !common.RedisEnabled {
|
|
return nil
|
|
}
|
|
|
|
return common.RedisHSetObj(
|
|
getUserCacheKey(user.Id),
|
|
user.ToBaseUser(),
|
|
time.Duration(common.RedisKeyCacheSeconds())*time.Second,
|
|
)
|
|
}
|
|
|
|
// GetUserCache gets complete user cache from hash
|
|
func GetUserCache(userId int) (userCache *UserBase, err error) {
|
|
var user *User
|
|
var fromDB bool
|
|
defer func() {
|
|
// Update Redis cache asynchronously on successful DB read
|
|
if shouldUpdateRedis(fromDB, err) && user != nil {
|
|
gopool.Go(func() {
|
|
if err := updateUserCache(*user); err != nil {
|
|
common.SysLog("failed to update user status cache: " + err.Error())
|
|
}
|
|
})
|
|
}
|
|
}()
|
|
|
|
// Try getting from Redis first
|
|
userCache, err = cacheGetUserBase(userId)
|
|
if err == nil {
|
|
return userCache, nil
|
|
}
|
|
|
|
// If Redis fails, get from DB
|
|
fromDB = true
|
|
user, err = GetUserById(userId, false)
|
|
if err != nil {
|
|
return nil, err // Return nil and error if DB lookup fails
|
|
}
|
|
|
|
// Create cache object from user data
|
|
userCache = &UserBase{
|
|
Id: user.Id,
|
|
Group: user.Group,
|
|
Quota: user.Quota,
|
|
Status: user.Status,
|
|
Username: user.Username,
|
|
Setting: user.Setting,
|
|
Email: user.Email,
|
|
}
|
|
|
|
return userCache, nil
|
|
}
|
|
|
|
func cacheGetUserBase(userId int) (*UserBase, error) {
|
|
if !common.RedisEnabled {
|
|
return nil, fmt.Errorf("redis is not enabled")
|
|
}
|
|
var userCache UserBase
|
|
// Try getting from Redis first
|
|
err := common.RedisHGetObj(getUserCacheKey(userId), &userCache)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &userCache, nil
|
|
}
|
|
|
|
// Add atomic quota operations using hash fields
|
|
func cacheIncrUserQuota(userId int, delta int64) error {
|
|
if !common.RedisEnabled {
|
|
return nil
|
|
}
|
|
return common.RedisHIncrBy(getUserCacheKey(userId), "Quota", delta)
|
|
}
|
|
|
|
func cacheDecrUserQuota(userId int, delta int64) error {
|
|
return cacheIncrUserQuota(userId, -delta)
|
|
}
|
|
|
|
// Helper functions to get individual fields if needed
|
|
func getUserGroupCache(userId int) (string, error) {
|
|
cache, err := GetUserCache(userId)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return cache.Group, nil
|
|
}
|
|
|
|
func getUserQuotaCache(userId int) (int, error) {
|
|
cache, err := GetUserCache(userId)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return cache.Quota, nil
|
|
}
|
|
|
|
func getUserStatusCache(userId int) (int, error) {
|
|
cache, err := GetUserCache(userId)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return cache.Status, nil
|
|
}
|
|
|
|
func getUserNameCache(userId int) (string, error) {
|
|
cache, err := GetUserCache(userId)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return cache.Username, nil
|
|
}
|
|
|
|
func getUserSettingCache(userId int) (dto.UserSetting, error) {
|
|
cache, err := GetUserCache(userId)
|
|
if err != nil {
|
|
return dto.UserSetting{}, err
|
|
}
|
|
return cache.GetSetting(), nil
|
|
}
|
|
|
|
// New functions for individual field updates
|
|
func updateUserStatusCache(userId int, status bool) error {
|
|
if !common.RedisEnabled {
|
|
return nil
|
|
}
|
|
statusInt := common.UserStatusEnabled
|
|
if !status {
|
|
statusInt = common.UserStatusDisabled
|
|
}
|
|
return common.RedisHSetField(getUserCacheKey(userId), "Status", fmt.Sprintf("%d", statusInt))
|
|
}
|
|
|
|
func updateUserQuotaCache(userId int, quota int) error {
|
|
if !common.RedisEnabled {
|
|
return nil
|
|
}
|
|
return common.RedisHSetField(getUserCacheKey(userId), "Quota", fmt.Sprintf("%d", quota))
|
|
}
|
|
|
|
func updateUserGroupCache(userId int, group string) error {
|
|
if !common.RedisEnabled {
|
|
return nil
|
|
}
|
|
return common.RedisHSetField(getUserCacheKey(userId), "Group", group)
|
|
}
|
|
|
|
func UpdateUserGroupCache(userId int, group string) error {
|
|
return updateUserGroupCache(userId, group)
|
|
}
|
|
|
|
func updateUserNameCache(userId int, username string) error {
|
|
if !common.RedisEnabled {
|
|
return nil
|
|
}
|
|
return common.RedisHSetField(getUserCacheKey(userId), "Username", username)
|
|
}
|
|
|
|
func updateUserSettingCache(userId int, setting string) error {
|
|
if !common.RedisEnabled {
|
|
return nil
|
|
}
|
|
return common.RedisHSetField(getUserCacheKey(userId), "Setting", setting)
|
|
}
|
|
|
|
// GetUserLanguage returns the user's language preference from cache
|
|
// Uses the existing GetUserCache mechanism for efficiency
|
|
func GetUserLanguage(userId int) string {
|
|
userCache, err := GetUserCache(userId)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return userCache.GetSetting().Language
|
|
}
|