package http import ( "context" "errors" "strings" "time" "github.com/gofiber/fiber/v3" "github.com/redis/go-redis/v9" ) type redisLimiterStorage struct { client redis.UniversalClient prefix string } var ( errRateLimitRedisConfigNil = errors.New("rate limit redis config is nil") errRateLimitRedisAddrsEmpty = errors.New("rate limit redis addrs is empty") ) func newRedisLimiterStorage(config *RateLimitRedis) (fiber.Storage, error) { if config == nil { return nil, errRateLimitRedisConfigNil } if len(config.Addrs) == 0 { return nil, errRateLimitRedisAddrsEmpty } client := redis.NewUniversalClient(&redis.UniversalOptions{ Addrs: config.Addrs, Username: config.Username, Password: config.Password, DB: config.DB, }) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() if err := client.Ping(ctx).Err(); err != nil { _ = client.Close() return nil, err } prefix := strings.TrimSpace(config.Prefix) return &redisLimiterStorage{ client: client, prefix: prefix, }, nil } func (s *redisLimiterStorage) GetWithContext(ctx context.Context, key string) ([]byte, error) { if s == nil || key == "" { return nil, nil } val, err := s.client.Get(ctx, s.key(key)).Bytes() if errors.Is(err, redis.Nil) { return nil, nil } return val, err } func (s *redisLimiterStorage) Get(key string) ([]byte, error) { return s.GetWithContext(context.Background(), key) } func (s *redisLimiterStorage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error { if s == nil || key == "" || len(val) == 0 { return nil } return s.client.Set(ctx, s.key(key), val, exp).Err() } func (s *redisLimiterStorage) Set(key string, val []byte, exp time.Duration) error { return s.SetWithContext(context.Background(), key, val, exp) } func (s *redisLimiterStorage) DeleteWithContext(ctx context.Context, key string) error { if s == nil || key == "" { return nil } return s.client.Del(ctx, s.key(key)).Err() } func (s *redisLimiterStorage) Delete(key string) error { return s.DeleteWithContext(context.Background(), key) } func (s *redisLimiterStorage) ResetWithContext(ctx context.Context) error { if s == nil { return nil } return s.client.FlushDB(ctx).Err() } func (s *redisLimiterStorage) Reset() error { return s.ResetWithContext(context.Background()) } func (s *redisLimiterStorage) Close() error { if s == nil { return nil } return s.client.Close() } func (s *redisLimiterStorage) key(raw string) string { if s.prefix == "" { return raw } return s.prefix + raw }