package auth import ( "context" "encoding/json" "errors" "strings" "time" "github.com/redis/go-redis/v9" "hyapp/pkg/appcode" ) 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 loginRiskIPDecisionKey(appCode string, clientIPHash string) string { return "login_risk:ip:" + appcode.Normalize(appCode) + ":" + strings.TrimSpace(clientIPHash) } func revokedSessionKey(appCode string, sessionID string) string { return "auth:revoked_session:" + appcode.Normalize(appCode) + ":" + strings.TrimSpace(sessionID) }