diff --git a/store/redis.go b/store/redis.go new file mode 100644 index 0000000..7bb47aa --- /dev/null +++ b/store/redis.go @@ -0,0 +1,96 @@ +package store + +import ( + "context" + "encoding/json" + "strings" + "time" + + "github.com/redis/go-redis/v9" +) + +type Store interface { + Get(ctx context.Context, key string, v any) error + Set(ctx context.Context, key string, v any, expireAt time.Time) error + SetTTL(ctx context.Context, key string, v any, ttl time.Duration) error + Delete(ctx context.Context, key string) error + Exists(ctx context.Context, key string) (bool, error) +} + +type Redis struct { + client *redis.Client + prefix string +} + +func NewRedis(addr, password, prefix string) (*Redis, error) { + var opts *redis.Options + + // unix socket detection + if strings.HasPrefix(addr, "/") { + opts = &redis.Options{ + Network: "unix", + Addr: addr, + Password: password, + } + } else { + opts = &redis.Options{ + Addr: addr, + Password: password, + } + } + + client := redis.NewClient(opts) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := client.Ping(ctx).Err(); err != nil { + return nil, err + } + + return &Redis{ + client: client, + prefix: prefix, + }, nil +} + +func (r *Redis) key(k string) string { + return r.prefix + "-" + k +} + +func (r *Redis) Get(ctx context.Context, key string, v any) error { + data, err := r.client.Get(ctx, r.key(key)).Bytes() + if err != nil { + return err + } + return json.Unmarshal(data, v) +} + +func (r *Redis) Set(ctx context.Context, key string, v any, expireAt time.Time) error { + data, err := json.Marshal(v) + if err != nil { + return err + } + return r.client.Set(ctx, r.key(key), data, time.Until(expireAt)).Err() +} + +func (r *Redis) SetTTL(ctx context.Context, key string, v any, ttl time.Duration) error { + data, err := json.Marshal(v) + if err != nil { + return err + } + return r.client.Set(ctx, r.key(key), data, ttl).Err() +} + +func (r *Redis) Delete(ctx context.Context, key string) error { + return r.client.Del(ctx, r.key(key)).Err() +} + +func (r *Redis) Exists(ctx context.Context, key string) (bool, error) { + n, err := r.client.Exists(ctx, r.key(key)).Result() + return n > 0, err +} + +func (r *Redis) Close() error { + return r.client.Close() +}