package store import ( "context" "encoding/json" "errors" "fmt" "time" "github.com/redis/go-redis/v9" "synctv/server/internal/room" ) var ErrNotFound = errors.New("room not found") type RedisStore struct { client *redis.Client ttl time.Duration } func NewRedis(addr, password string, db int, ttl time.Duration) *RedisStore { return &RedisStore{ client: redis.NewClient(&redis.Options{ Addr: addr, Password: password, DB: db, }), ttl: ttl, } } func (s *RedisStore) Ping(ctx context.Context) error { return s.client.Ping(ctx).Err() } func (s *RedisStore) Close() error { return s.client.Close() } func (s *RedisStore) GetRoom(ctx context.Context, code string) (room.Room, error) { b, err := s.client.Get(ctx, roomKey(code)).Bytes() if errors.Is(err, redis.Nil) { return room.Room{}, ErrNotFound } if err != nil { return room.Room{}, err } var r room.Room if err := json.Unmarshal(b, &r); err != nil { return room.Room{}, err } return r, nil } func (s *RedisStore) SaveRoom(ctx context.Context, r room.Room) error { r.UpdatedAt = room.NowMS() r.ExpiresAt = time.Now().Add(s.ttl).UnixMilli() b, err := json.Marshal(r) if err != nil { return err } return s.client.Set(ctx, roomKey(r.Code), b, s.ttl).Err() } func (s *RedisStore) CreateRoom(ctx context.Context, r room.Room) error { b, err := json.Marshal(r) if err != nil { return err } ok, err := s.client.SetNX(ctx, roomKey(r.Code), b, s.ttl).Result() if err != nil { return err } if !ok { return errors.New("room code already exists") } return nil } func roomKey(code string) string { return fmt.Sprintf("room:%s", code) }