cache.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. package user
  2. import (
  3. "context"
  4. "fmt"
  5. "sync"
  6. "time"
  7. "github.com/0xJacky/Nginx-UI/internal/cache"
  8. "github.com/0xJacky/Nginx-UI/model"
  9. "github.com/0xJacky/Nginx-UI/query"
  10. "github.com/uozi-tech/cosy/logger"
  11. )
  12. const (
  13. // Cache key prefixes
  14. tokenCachePrefix = "auth_token:"
  15. shortTokenCachePrefix = "short_token:"
  16. userCachePrefix = "user:"
  17. // Cache TTL
  18. tokenCacheTTL = 24 * time.Hour
  19. )
  20. // TokenCacheData stores token information in cache
  21. type TokenCacheData struct {
  22. UserID uint64 `json:"user_id"`
  23. Token string `json:"token"`
  24. ShortToken string `json:"short_token"`
  25. ExpiredAt int64 `json:"expired_at"`
  26. CreatedAt time.Time `json:"created_at"`
  27. }
  28. // UserCacheData stores user information in cache
  29. type UserCacheData struct {
  30. *model.User
  31. CachedAt time.Time `json:"cached_at"`
  32. }
  33. var (
  34. cacheMutex = &sync.RWMutex{}
  35. )
  36. // InitTokenCache loads all active tokens into cache on startup
  37. func InitTokenCache(ctx context.Context) {
  38. logger.Info("Initializing token cache...")
  39. q := query.AuthToken
  40. authTokens, err := q.Where(q.ExpiredAt.Gte(time.Now().Unix())).Find()
  41. if err != nil {
  42. logger.Error("Failed to load auth tokens:", err)
  43. return
  44. }
  45. cacheMutex.Lock()
  46. defer cacheMutex.Unlock()
  47. loaded := 0
  48. for _, authToken := range authTokens {
  49. cacheData := &TokenCacheData{
  50. UserID: authToken.UserID,
  51. Token: authToken.Token,
  52. ShortToken: authToken.ShortToken,
  53. ExpiredAt: authToken.ExpiredAt,
  54. CreatedAt: time.Now(),
  55. }
  56. // Cache by token
  57. if authToken.Token != "" {
  58. tokenKey := tokenCachePrefix + authToken.Token
  59. cache.Set(tokenKey, cacheData, tokenCacheTTL)
  60. }
  61. // Cache by short token
  62. if authToken.ShortToken != "" {
  63. shortTokenKey := shortTokenCachePrefix + authToken.ShortToken
  64. cache.Set(shortTokenKey, cacheData, tokenCacheTTL)
  65. }
  66. loaded++
  67. }
  68. logger.Info(fmt.Sprintf("Loaded %d auth tokens into cache", loaded))
  69. }
  70. // CacheToken stores a token in cache
  71. func CacheToken(authToken *model.AuthToken) {
  72. if authToken == nil {
  73. return
  74. }
  75. cacheMutex.Lock()
  76. defer cacheMutex.Unlock()
  77. cacheData := &TokenCacheData{
  78. UserID: authToken.UserID,
  79. Token: authToken.Token,
  80. ShortToken: authToken.ShortToken,
  81. ExpiredAt: authToken.ExpiredAt,
  82. CreatedAt: time.Now(),
  83. }
  84. // Cache by token
  85. if authToken.Token != "" {
  86. tokenKey := tokenCachePrefix + authToken.Token
  87. cache.Set(tokenKey, cacheData, tokenCacheTTL)
  88. }
  89. // Cache by short token
  90. if authToken.ShortToken != "" {
  91. shortTokenKey := shortTokenCachePrefix + authToken.ShortToken
  92. cache.Set(shortTokenKey, cacheData, tokenCacheTTL)
  93. }
  94. }
  95. // GetCachedTokenData retrieves token data from cache
  96. func GetCachedTokenData(token string) (*TokenCacheData, bool) {
  97. cacheMutex.RLock()
  98. defer cacheMutex.RUnlock()
  99. tokenKey := tokenCachePrefix + token
  100. data, found := cache.Get(tokenKey)
  101. if !found {
  102. return nil, false
  103. }
  104. tokenData, ok := data.(*TokenCacheData)
  105. if !ok {
  106. // Invalid cache data, remove it
  107. cache.Del(tokenKey)
  108. return nil, false
  109. }
  110. // Check if token is expired
  111. if tokenData.ExpiredAt < time.Now().Unix() {
  112. // Token expired, remove from cache
  113. cache.Del(tokenKey)
  114. if tokenData.ShortToken != "" {
  115. cache.Del(shortTokenCachePrefix + tokenData.ShortToken)
  116. }
  117. return nil, false
  118. }
  119. return tokenData, true
  120. }
  121. // GetCachedShortTokenData retrieves short token data from cache
  122. func GetCachedShortTokenData(shortToken string) (*TokenCacheData, bool) {
  123. cacheMutex.RLock()
  124. defer cacheMutex.RUnlock()
  125. shortTokenKey := shortTokenCachePrefix + shortToken
  126. data, found := cache.Get(shortTokenKey)
  127. if !found {
  128. return nil, false
  129. }
  130. tokenData, ok := data.(*TokenCacheData)
  131. if !ok {
  132. // Invalid cache data, remove it
  133. cache.Del(shortTokenKey)
  134. return nil, false
  135. }
  136. // Check if token is expired
  137. if tokenData.ExpiredAt < time.Now().Unix() {
  138. // Token expired, remove from cache
  139. cache.Del(shortTokenKey)
  140. if tokenData.Token != "" {
  141. cache.Del(tokenCachePrefix + tokenData.Token)
  142. }
  143. return nil, false
  144. }
  145. return tokenData, true
  146. }
  147. // CacheUser stores user data in cache
  148. func CacheUser(user *model.User) {
  149. if user == nil {
  150. return
  151. }
  152. cacheMutex.Lock()
  153. defer cacheMutex.Unlock()
  154. userKey := fmt.Sprintf("%s%d", userCachePrefix, user.ID)
  155. cacheData := &UserCacheData{
  156. User: user,
  157. CachedAt: time.Now(),
  158. }
  159. cache.Set(userKey, cacheData, tokenCacheTTL)
  160. }
  161. // GetCachedUser retrieves user data from cache
  162. func GetCachedUser(userID uint64) (*model.User, bool) {
  163. cacheMutex.RLock()
  164. defer cacheMutex.RUnlock()
  165. userKey := fmt.Sprintf("%s%d", userCachePrefix, userID)
  166. data, found := cache.Get(userKey)
  167. if !found {
  168. return nil, false
  169. }
  170. userData, ok := data.(*UserCacheData)
  171. if !ok {
  172. // Invalid cache data, remove it
  173. cache.Del(userKey)
  174. return nil, false
  175. }
  176. // Check if cache is too old (refresh every hour)
  177. if time.Since(userData.CachedAt) > time.Hour {
  178. cache.Del(userKey)
  179. return nil, false
  180. }
  181. return userData.User, true
  182. }
  183. // InvalidateTokenCache removes token from cache
  184. func InvalidateTokenCache(token string) {
  185. cacheMutex.Lock()
  186. defer cacheMutex.Unlock()
  187. // Try to get token data first to also remove short token
  188. tokenKey := tokenCachePrefix + token
  189. if data, found := cache.Get(tokenKey); found {
  190. if tokenData, ok := data.(*TokenCacheData); ok && tokenData.ShortToken != "" {
  191. cache.Del(shortTokenCachePrefix + tokenData.ShortToken)
  192. }
  193. }
  194. cache.Del(tokenKey)
  195. }
  196. // InvalidateUserCache removes user from cache
  197. func InvalidateUserCache(userID uint64) {
  198. cacheMutex.Lock()
  199. defer cacheMutex.Unlock()
  200. userKey := fmt.Sprintf("%s%d", userCachePrefix, userID)
  201. cache.Del(userKey)
  202. }
  203. // ClearExpiredTokens removes expired tokens from cache
  204. func ClearExpiredTokens() {
  205. cacheMutex.Lock()
  206. defer cacheMutex.Unlock()
  207. now := time.Now().Unix()
  208. // Note: ristretto doesn't provide a way to iterate over all keys
  209. // Expired tokens will be removed when accessed via GetCachedTokenData/GetCachedShortTokenData
  210. // or when the cache reaches capacity limits
  211. logger.Debug(fmt.Sprintf("Cache cleanup completed at %d", now))
  212. }