package redis import ( "context" "encoding/json" "time" "github.com/redis/go-redis/v9" "github.com/rwadurian/mpc-system/services/account/application/ports" ) // CacheAdapter implements CacheService using Redis type CacheAdapter struct { client *redis.Client } // NewCacheAdapter creates a new CacheAdapter func NewCacheAdapter(client *redis.Client) ports.CacheService { return &CacheAdapter{client: client} } // Set sets a value in the cache func (c *CacheAdapter) Set(ctx context.Context, key string, value interface{}, ttlSeconds int) error { data, err := json.Marshal(value) if err != nil { return err } return c.client.Set(ctx, key, data, time.Duration(ttlSeconds)*time.Second).Err() } // Get gets a value from the cache func (c *CacheAdapter) Get(ctx context.Context, key string) (interface{}, error) { data, err := c.client.Get(ctx, key).Bytes() if err != nil { if err == redis.Nil { return nil, nil } return nil, err } var value interface{} if err := json.Unmarshal(data, &value); err != nil { return nil, err } return value, nil } // Delete deletes a value from the cache func (c *CacheAdapter) Delete(ctx context.Context, key string) error { return c.client.Del(ctx, key).Err() } // Exists checks if a key exists in the cache func (c *CacheAdapter) Exists(ctx context.Context, key string) (bool, error) { result, err := c.client.Exists(ctx, key).Result() if err != nil { return false, err } return result > 0, nil } // AccountCacheAdapter provides account-specific caching type AccountCacheAdapter struct { client *redis.Client keyPrefix string } // NewAccountCacheAdapter creates a new AccountCacheAdapter func NewAccountCacheAdapter(client *redis.Client) *AccountCacheAdapter { return &AccountCacheAdapter{ client: client, keyPrefix: "account:", } } // CacheAccount caches an account func (c *AccountCacheAdapter) CacheAccount(ctx context.Context, accountID string, data interface{}, ttl time.Duration) error { key := c.keyPrefix + accountID jsonData, err := json.Marshal(data) if err != nil { return err } return c.client.Set(ctx, key, jsonData, ttl).Err() } // GetCachedAccount gets a cached account func (c *AccountCacheAdapter) GetCachedAccount(ctx context.Context, accountID string) (map[string]interface{}, error) { key := c.keyPrefix + accountID data, err := c.client.Get(ctx, key).Bytes() if err != nil { if err == redis.Nil { return nil, nil } return nil, err } var result map[string]interface{} if err := json.Unmarshal(data, &result); err != nil { return nil, err } return result, nil } // InvalidateAccount invalidates cached account data func (c *AccountCacheAdapter) InvalidateAccount(ctx context.Context, accountID string) error { key := c.keyPrefix + accountID return c.client.Del(ctx, key).Err() } // CacheLoginChallenge caches a login challenge func (c *AccountCacheAdapter) CacheLoginChallenge(ctx context.Context, challengeID string, data map[string]interface{}) error { key := "login_challenge:" + challengeID jsonData, err := json.Marshal(data) if err != nil { return err } return c.client.Set(ctx, key, jsonData, 5*time.Minute).Err() } // GetLoginChallenge gets a login challenge func (c *AccountCacheAdapter) GetLoginChallenge(ctx context.Context, challengeID string) (map[string]interface{}, error) { key := "login_challenge:" + challengeID data, err := c.client.Get(ctx, key).Bytes() if err != nil { if err == redis.Nil { return nil, nil } return nil, err } var result map[string]interface{} if err := json.Unmarshal(data, &result); err != nil { return nil, err } return result, nil } // DeleteLoginChallenge deletes a login challenge after use func (c *AccountCacheAdapter) DeleteLoginChallenge(ctx context.Context, challengeID string) error { key := "login_challenge:" + challengeID return c.client.Del(ctx, key).Err() } // IncrementLoginAttempts increments failed login attempts func (c *AccountCacheAdapter) IncrementLoginAttempts(ctx context.Context, username string) (int64, error) { key := "login_attempts:" + username count, err := c.client.Incr(ctx, key).Result() if err != nil { return 0, err } // Set expiry on first attempt if count == 1 { c.client.Expire(ctx, key, 15*time.Minute) } return count, nil } // GetLoginAttempts gets the current login attempt count func (c *AccountCacheAdapter) GetLoginAttempts(ctx context.Context, username string) (int64, error) { key := "login_attempts:" + username count, err := c.client.Get(ctx, key).Int64() if err != nil { if err == redis.Nil { return 0, nil } return 0, err } return count, nil } // ResetLoginAttempts resets login attempts after successful login func (c *AccountCacheAdapter) ResetLoginAttempts(ctx context.Context, username string) error { key := "login_attempts:" + username return c.client.Del(ctx, key).Err() }