rwadurian/backend/mpc-system/services/account/adapters/output/redis/cache_adapter.go

182 lines
4.9 KiB
Go

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