package auth import ( "context" "encoding/json" "errors" "strings" "time" "github.com/redis/go-redis/v9" "hyapp/pkg/appcode" authdomain "hyapp/services/user-service/internal/domain/auth" ) type RedisIPDecisionCache struct { client *redis.Client } func NewRedisIPDecisionCache(client *redis.Client) *RedisIPDecisionCache { return &RedisIPDecisionCache{client: client} } func NewLoginRiskRedisClient(ctx context.Context, addr string, password string, db int) (*redis.Client, error) { client := redis.NewClient(&redis.Options{ Addr: strings.TrimSpace(addr), Password: password, DB: db, }) if err := client.Ping(ctx).Err(); err != nil { _ = client.Close() return nil, err } return client, nil } func (c *RedisIPDecisionCache) GetIPDecision(ctx context.Context, appCode string, clientIPHash string) (IPDecision, bool, error) { raw, err := c.client.Get(ctx, loginRiskIPDecisionKey(appCode, clientIPHash)).Result() if errors.Is(err, redis.Nil) { return IPDecision{}, false, nil } if err != nil { return IPDecision{}, false, err } var decision IPDecision if err := json.Unmarshal([]byte(raw), &decision); err != nil { return IPDecision{}, false, err } return decision, true, nil } func (c *RedisIPDecisionCache) SetIPDecision(ctx context.Context, appCode string, clientIPHash string, decision IPDecision, ttl time.Duration) error { raw, err := json.Marshal(decision) if err != nil { return err } return c.client.Set(ctx, loginRiskIPDecisionKey(appCode, clientIPHash), raw, ttl).Err() } func (c *RedisIPDecisionCache) SetRevokedSession(ctx context.Context, appCode string, sessionID string, reason string, ttl time.Duration) error { return c.client.Set(ctx, revokedSessionKey(appCode, sessionID), strings.TrimSpace(reason), ttl).Err() } func (c *RedisIPDecisionCache) GetIPWhitelist(ctx context.Context, appCode string) ([]string, bool, error) { raw, err := c.client.Get(ctx, loginRiskIPWhitelistKey(appCode)).Result() if errors.Is(err, redis.Nil) { return nil, false, nil } if err != nil { return nil, false, err } var ips []string if err := json.Unmarshal([]byte(raw), &ips); err != nil { return nil, false, err } return ips, true, nil } func (c *RedisIPDecisionCache) SetIPWhitelist(ctx context.Context, appCode string, ips []string, ttl time.Duration) error { raw, err := json.Marshal(ips) if err != nil { return err } return c.client.Set(ctx, loginRiskIPWhitelistKey(appCode), raw, ttl).Err() } func (c *RedisIPDecisionCache) GetRegisterRiskConfig(ctx context.Context, appCode string) (authdomain.RegisterRiskConfig, bool, error) { raw, err := c.client.Get(ctx, registerRiskConfigKey(appCode)).Result() if errors.Is(err, redis.Nil) { return authdomain.RegisterRiskConfig{}, false, nil } if err != nil { return authdomain.RegisterRiskConfig{}, false, err } var config authdomain.RegisterRiskConfig if err := json.Unmarshal([]byte(raw), &config); err != nil { return authdomain.RegisterRiskConfig{}, false, err } return config, true, nil } func (c *RedisIPDecisionCache) SetRegisterRiskConfig(ctx context.Context, config authdomain.RegisterRiskConfig, ttl time.Duration) error { raw, err := json.Marshal(config) if err != nil { return err } return c.client.Set(ctx, registerRiskConfigKey(config.AppCode), raw, ttl).Err() } func loginRiskIPDecisionKey(appCode string, clientIPHash string) string { return "login_risk:ip:" + appcode.Normalize(appCode) + ":" + strings.TrimSpace(clientIPHash) } func loginRiskIPWhitelistKey(appCode string) string { return "login_risk:ip_whitelist:" + appcode.Normalize(appCode) } func revokedSessionKey(appCode string, sessionID string) string { return "auth:revoked_session:" + appcode.Normalize(appCode) + ":" + strings.TrimSpace(sessionID) } func registerRiskConfigKey(appCode string) string { return "auth:risk_config:register:" + appcode.Normalize(appCode) }