mirror of
https://github.com/Dvorinka/SEEN.git
synced 2026-06-04 12:33:02 +00:00
small fix, don't worry about it
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// SessionData represents cached session information
|
||||
type SessionData struct {
|
||||
SessionID string `json:"sessionId"`
|
||||
UserID string `json:"userId"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
IP string `json:"ip"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
// SessionCache provides session caching operations
|
||||
type SessionCache struct {
|
||||
service *Service
|
||||
keys *KeyBuilder
|
||||
}
|
||||
|
||||
// NewSessionCache creates a new session cache
|
||||
func NewSessionCache(service *Service, namespace string) *SessionCache {
|
||||
return &SessionCache{
|
||||
service: service,
|
||||
keys: NewKeyBuilder(namespace),
|
||||
}
|
||||
}
|
||||
|
||||
// SetSession stores session data in cache
|
||||
func (sc *SessionCache) SetSession(ctx context.Context, session SessionData) error {
|
||||
key := sc.keys.SessionKey(session.SessionID)
|
||||
ttl := time.Until(session.ExpiresAt)
|
||||
|
||||
if ttl <= 0 {
|
||||
return fmt.Errorf("session already expired")
|
||||
}
|
||||
|
||||
return sc.service.Set(ctx, key, session, ttl)
|
||||
}
|
||||
|
||||
// GetSession retrieves session data from cache
|
||||
func (sc *SessionCache) GetSession(ctx context.Context, sessionID string) (*SessionData, error) {
|
||||
key := sc.keys.SessionKey(sessionID)
|
||||
var session SessionData
|
||||
|
||||
if err := sc.service.Get(ctx, key, &session); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &session, nil
|
||||
}
|
||||
|
||||
// DeleteSession removes session data from cache
|
||||
func (sc *SessionCache) DeleteSession(ctx context.Context, sessionID string) error {
|
||||
key := sc.keys.SessionKey(sessionID)
|
||||
return sc.service.Delete(ctx, key)
|
||||
}
|
||||
|
||||
// GetSessionByRefreshToken retrieves session by refresh token
|
||||
// Note: This requires scanning, which is slower. Consider using a secondary index.
|
||||
func (sc *SessionCache) GetSessionByRefreshToken(ctx context.Context, refreshToken string) (*SessionData, error) {
|
||||
pattern := sc.keys.Build(PrefixSession, "*")
|
||||
keys, err := sc.service.Keys(ctx, pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
var session SessionData
|
||||
if err := sc.service.Get(ctx, key, &session); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if session.RefreshToken == refreshToken {
|
||||
return &session, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrCacheMiss
|
||||
}
|
||||
|
||||
// ExtendSession extends the TTL of a session
|
||||
func (sc *SessionCache) ExtendSession(ctx context.Context, sessionID string, duration time.Duration) error {
|
||||
key := sc.keys.SessionKey(sessionID)
|
||||
return sc.service.Expire(ctx, key, duration)
|
||||
}
|
||||
|
||||
// UserData represents cached user information
|
||||
type UserData struct {
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Role string `json:"role"`
|
||||
CachedAt time.Time `json:"cachedAt"`
|
||||
}
|
||||
|
||||
// SetUser stores user data in cache
|
||||
func (sc *SessionCache) SetUser(ctx context.Context, user UserData) error {
|
||||
key := sc.keys.UserKey(user.ID)
|
||||
return sc.service.Set(ctx, key, user, TTLUser)
|
||||
}
|
||||
|
||||
// GetUser retrieves user data from cache
|
||||
func (sc *SessionCache) GetUser(ctx context.Context, userID string) (*UserData, error) {
|
||||
key := sc.keys.UserKey(userID)
|
||||
var user UserData
|
||||
|
||||
if err := sc.service.Get(ctx, key, &user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// DeleteUser removes user data from cache
|
||||
func (sc *SessionCache) DeleteUser(ctx context.Context, userID string) error {
|
||||
key := sc.keys.UserKey(userID)
|
||||
return sc.service.Delete(ctx, key)
|
||||
}
|
||||
|
||||
// InvalidateUserSessions removes all sessions for a user
|
||||
func (sc *SessionCache) InvalidateUserSessions(ctx context.Context, userID string) error {
|
||||
pattern := sc.keys.Build(PrefixSession, "*")
|
||||
keys, err := sc.service.Keys(ctx, pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
toDelete := make([]string, 0)
|
||||
for _, key := range keys {
|
||||
var session SessionData
|
||||
if err := sc.service.Get(ctx, key, &session); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if session.UserID == userID {
|
||||
toDelete = append(toDelete, key)
|
||||
}
|
||||
}
|
||||
|
||||
if len(toDelete) > 0 {
|
||||
return sc.service.Delete(ctx, toDelete...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RateLimitCheck checks if an action is rate limited
|
||||
func (sc *SessionCache) RateLimitCheck(ctx context.Context, identifier, action string, limit int64, window time.Duration) (bool, error) {
|
||||
key := sc.keys.RateLimitKey(identifier, action)
|
||||
|
||||
count, err := sc.service.Increment(ctx, key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Set expiry on first increment
|
||||
if count == 1 {
|
||||
if err := sc.service.Expire(ctx, key, window); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return count <= limit, nil
|
||||
}
|
||||
|
||||
// AcquireLock attempts to acquire a distributed lock
|
||||
func (sc *SessionCache) AcquireLock(ctx context.Context, resource string, ttl time.Duration) (string, bool, error) {
|
||||
lockID := uuid.New().String()
|
||||
key := sc.keys.LockKey(resource)
|
||||
|
||||
acquired, err := sc.service.SetNX(ctx, key, lockID, ttl)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
return lockID, acquired, nil
|
||||
}
|
||||
|
||||
// ReleaseLock releases a distributed lock
|
||||
func (sc *SessionCache) ReleaseLock(ctx context.Context, resource, lockID string) error {
|
||||
key := sc.keys.LockKey(resource)
|
||||
|
||||
// Verify we own the lock before deleting
|
||||
var storedLockID string
|
||||
if err := sc.service.Get(ctx, key, &storedLockID); err != nil {
|
||||
if err == ErrCacheMiss {
|
||||
return nil // Lock already released
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if storedLockID != lockID {
|
||||
return fmt.Errorf("lock owned by different process")
|
||||
}
|
||||
|
||||
return sc.service.Delete(ctx, key)
|
||||
}
|
||||
Reference in New Issue
Block a user