123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- 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))
- }
|