mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2026-05-08 06:52:10 +08:00
260 lines
5.9 KiB
Go
260 lines
5.9 KiB
Go
package user
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/0xJacky/Nginx-UI/internal/cache"
|
|
"github.com/0xJacky/Nginx-UI/model"
|
|
"github.com/0xJacky/Nginx-UI/query"
|
|
"github.com/uozi-tech/cosy/logger"
|
|
)
|
|
|
|
const (
|
|
// Cache key prefixes
|
|
tokenCachePrefix = "auth_token:"
|
|
shortTokenCachePrefix = "short_token:"
|
|
userCachePrefix = "user:"
|
|
|
|
// Cache TTL
|
|
tokenCacheTTL = 24 * time.Hour
|
|
)
|
|
|
|
// TokenCacheData stores token information in cache
|
|
type TokenCacheData struct {
|
|
UserID uint64 `json:"user_id"`
|
|
Token string `json:"token"`
|
|
ShortToken string `json:"short_token"`
|
|
ExpiredAt int64 `json:"expired_at"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
// UserCacheData stores user information in cache
|
|
type UserCacheData struct {
|
|
*model.User
|
|
CachedAt time.Time `json:"cached_at"`
|
|
}
|
|
|
|
var (
|
|
cacheMutex = &sync.RWMutex{}
|
|
)
|
|
|
|
// InitTokenCache loads all active tokens into cache on startup
|
|
func InitTokenCache(ctx context.Context) {
|
|
logger.Info("Initializing token cache...")
|
|
|
|
q := query.AuthToken
|
|
authTokens, err := q.Where(q.ExpiredAt.Gte(time.Now().Unix())).Find()
|
|
if err != nil {
|
|
logger.Error("Failed to load auth tokens:", err)
|
|
return
|
|
}
|
|
|
|
cacheMutex.Lock()
|
|
defer cacheMutex.Unlock()
|
|
|
|
loaded := 0
|
|
for _, authToken := range authTokens {
|
|
cacheData := &TokenCacheData{
|
|
UserID: authToken.UserID,
|
|
Token: authToken.Token,
|
|
ShortToken: authToken.ShortToken,
|
|
ExpiredAt: authToken.ExpiredAt,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
|
|
// Cache by token
|
|
if authToken.Token != "" {
|
|
tokenKey := tokenCachePrefix + authToken.Token
|
|
cache.Set(tokenKey, cacheData, tokenCacheTTL)
|
|
}
|
|
|
|
// Cache by short token
|
|
if authToken.ShortToken != "" {
|
|
shortTokenKey := shortTokenCachePrefix + authToken.ShortToken
|
|
cache.Set(shortTokenKey, cacheData, tokenCacheTTL)
|
|
}
|
|
|
|
loaded++
|
|
}
|
|
|
|
logger.Info(fmt.Sprintf("Loaded %d auth tokens into cache", loaded))
|
|
}
|
|
|
|
// CacheToken stores a token in cache
|
|
func CacheToken(authToken *model.AuthToken) {
|
|
if authToken == nil {
|
|
return
|
|
}
|
|
|
|
cacheMutex.Lock()
|
|
defer cacheMutex.Unlock()
|
|
|
|
cacheData := &TokenCacheData{
|
|
UserID: authToken.UserID,
|
|
Token: authToken.Token,
|
|
ShortToken: authToken.ShortToken,
|
|
ExpiredAt: authToken.ExpiredAt,
|
|
CreatedAt: time.Now(),
|
|
}
|
|
|
|
// Cache by token
|
|
if authToken.Token != "" {
|
|
tokenKey := tokenCachePrefix + authToken.Token
|
|
cache.Set(tokenKey, cacheData, tokenCacheTTL)
|
|
}
|
|
|
|
// Cache by short token
|
|
if authToken.ShortToken != "" {
|
|
shortTokenKey := shortTokenCachePrefix + authToken.ShortToken
|
|
cache.Set(shortTokenKey, cacheData, tokenCacheTTL)
|
|
}
|
|
}
|
|
|
|
// GetCachedTokenData retrieves token data from cache
|
|
func GetCachedTokenData(token string) (*TokenCacheData, bool) {
|
|
cacheMutex.RLock()
|
|
defer cacheMutex.RUnlock()
|
|
|
|
tokenKey := tokenCachePrefix + token
|
|
data, found := cache.Get(tokenKey)
|
|
if !found {
|
|
return nil, false
|
|
}
|
|
|
|
tokenData, ok := data.(*TokenCacheData)
|
|
if !ok {
|
|
// Invalid cache data, remove it
|
|
cache.Del(tokenKey)
|
|
return nil, false
|
|
}
|
|
|
|
// Check if token is expired
|
|
if tokenData.ExpiredAt < time.Now().Unix() {
|
|
// Token expired, remove from cache
|
|
cache.Del(tokenKey)
|
|
if tokenData.ShortToken != "" {
|
|
cache.Del(shortTokenCachePrefix + tokenData.ShortToken)
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
return tokenData, true
|
|
}
|
|
|
|
// GetCachedShortTokenData retrieves short token data from cache
|
|
func GetCachedShortTokenData(shortToken string) (*TokenCacheData, bool) {
|
|
cacheMutex.RLock()
|
|
defer cacheMutex.RUnlock()
|
|
|
|
shortTokenKey := shortTokenCachePrefix + shortToken
|
|
data, found := cache.Get(shortTokenKey)
|
|
if !found {
|
|
return nil, false
|
|
}
|
|
|
|
tokenData, ok := data.(*TokenCacheData)
|
|
if !ok {
|
|
// Invalid cache data, remove it
|
|
cache.Del(shortTokenKey)
|
|
return nil, false
|
|
}
|
|
|
|
// Check if token is expired
|
|
if tokenData.ExpiredAt < time.Now().Unix() {
|
|
// Token expired, remove from cache
|
|
cache.Del(shortTokenKey)
|
|
if tokenData.Token != "" {
|
|
cache.Del(tokenCachePrefix + tokenData.Token)
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
return tokenData, true
|
|
}
|
|
|
|
// CacheUser stores user data in cache
|
|
func CacheUser(user *model.User) {
|
|
if user == nil {
|
|
return
|
|
}
|
|
|
|
cacheMutex.Lock()
|
|
defer cacheMutex.Unlock()
|
|
|
|
userKey := fmt.Sprintf("%s%d", userCachePrefix, user.ID)
|
|
cacheData := &UserCacheData{
|
|
User: user,
|
|
CachedAt: time.Now(),
|
|
}
|
|
|
|
cache.Set(userKey, cacheData, tokenCacheTTL)
|
|
}
|
|
|
|
// GetCachedUser retrieves user data from cache
|
|
func GetCachedUser(userID uint64) (*model.User, bool) {
|
|
cacheMutex.RLock()
|
|
defer cacheMutex.RUnlock()
|
|
|
|
userKey := fmt.Sprintf("%s%d", userCachePrefix, userID)
|
|
data, found := cache.Get(userKey)
|
|
if !found {
|
|
return nil, false
|
|
}
|
|
|
|
userData, ok := data.(*UserCacheData)
|
|
if !ok {
|
|
// Invalid cache data, remove it
|
|
cache.Del(userKey)
|
|
return nil, false
|
|
}
|
|
|
|
// Check if cache is too old (refresh every hour)
|
|
if time.Since(userData.CachedAt) > time.Hour {
|
|
cache.Del(userKey)
|
|
return nil, false
|
|
}
|
|
|
|
return userData.User, true
|
|
}
|
|
|
|
// InvalidateTokenCache removes token from cache
|
|
func InvalidateTokenCache(token string) {
|
|
cacheMutex.Lock()
|
|
defer cacheMutex.Unlock()
|
|
|
|
// Try to get token data first to also remove short token
|
|
tokenKey := tokenCachePrefix + token
|
|
if data, found := cache.Get(tokenKey); found {
|
|
if tokenData, ok := data.(*TokenCacheData); ok && tokenData.ShortToken != "" {
|
|
cache.Del(shortTokenCachePrefix + tokenData.ShortToken)
|
|
}
|
|
}
|
|
|
|
cache.Del(tokenKey)
|
|
}
|
|
|
|
// InvalidateUserCache removes user from cache
|
|
func InvalidateUserCache(userID uint64) {
|
|
cacheMutex.Lock()
|
|
defer cacheMutex.Unlock()
|
|
|
|
userKey := fmt.Sprintf("%s%d", userCachePrefix, userID)
|
|
cache.Del(userKey)
|
|
}
|
|
|
|
// ClearExpiredTokens removes expired tokens from cache
|
|
func ClearExpiredTokens() {
|
|
cacheMutex.Lock()
|
|
defer cacheMutex.Unlock()
|
|
|
|
now := time.Now().Unix()
|
|
|
|
// Note: ristretto doesn't provide a way to iterate over all keys
|
|
// Expired tokens will be removed when accessed via GetCachedTokenData/GetCachedShortTokenData
|
|
// or when the cache reaches capacity limits
|
|
|
|
logger.Debug(fmt.Sprintf("Cache cleanup completed at %d", now))
|
|
} |