init
This commit is contained in:
139
server/internal/room/service.go
Normal file
139
server/internal/room/service.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package room
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
||||
|
||||
type Store interface {
|
||||
CreateRoom(ctx context.Context, r Room) error
|
||||
GetRoom(ctx context.Context, code string) (Room, error)
|
||||
SaveRoom(ctx context.Context, r Room) error
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
store Store
|
||||
codeLength int
|
||||
emptyTTL time.Duration
|
||||
ownerReconnectHold time.Duration
|
||||
}
|
||||
|
||||
func NewService(st Store, codeLength int, emptyTTL, ownerReconnectHold time.Duration) *Service {
|
||||
return &Service{
|
||||
store: st,
|
||||
codeLength: codeLength,
|
||||
emptyTTL: emptyTTL,
|
||||
ownerReconnectHold: ownerReconnectHold,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context) (Room, error) {
|
||||
for i := 0; i < 10; i++ {
|
||||
code, err := randomCode(s.codeLength)
|
||||
if err != nil {
|
||||
return Room{}, err
|
||||
}
|
||||
r := New(code, s.emptyTTL)
|
||||
if err := s.store.CreateRoom(ctx, r); err == nil {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
return Room{}, errors.New("failed to allocate room code")
|
||||
}
|
||||
|
||||
func (s *Service) Join(ctx context.Context, code, deviceID string, onlineCount int) (Room, bool, error) {
|
||||
code = strings.ToUpper(strings.TrimSpace(code))
|
||||
if code == "" || deviceID == "" {
|
||||
return Room{}, false, errors.New("room code and device id are required")
|
||||
}
|
||||
|
||||
r, err := s.store.GetRoom(ctx, code)
|
||||
if err != nil {
|
||||
return Room{}, false, err
|
||||
}
|
||||
now := NowMS()
|
||||
if r.OwnerDeviceID == "" {
|
||||
r.OwnerDeviceID = deviceID
|
||||
}
|
||||
if r.OwnerDeviceID == deviceID {
|
||||
r.OwnerLastSeenAt = now
|
||||
}
|
||||
r.OnlineCount = onlineCount
|
||||
if err := s.store.SaveRoom(ctx, r); err != nil {
|
||||
return Room{}, false, err
|
||||
}
|
||||
return r, r.OwnerDeviceID == deviceID, nil
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, code string) (Room, error) {
|
||||
return s.store.GetRoom(ctx, strings.ToUpper(strings.TrimSpace(code)))
|
||||
}
|
||||
|
||||
func (s *Service) SetOnlineCount(ctx context.Context, code string, count int) error {
|
||||
r, err := s.Get(ctx, code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.OnlineCount = count
|
||||
return s.store.SaveRoom(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Service) SetSource(ctx context.Context, code, deviceID string, src Source) (Room, error) {
|
||||
r, err := s.Get(ctx, code)
|
||||
if err != nil {
|
||||
return Room{}, err
|
||||
}
|
||||
if r.OwnerDeviceID != deviceID {
|
||||
return Room{}, errors.New("only room owner can configure source")
|
||||
}
|
||||
if src.Mode == "" {
|
||||
src.Mode = SourceModeDirect
|
||||
}
|
||||
if src.Mode != SourceModeDirect && src.Mode != SourceModeProxy {
|
||||
return Room{}, errors.New("source mode must be direct or proxy")
|
||||
}
|
||||
if strings.TrimSpace(src.URL) == "" {
|
||||
return Room{}, errors.New("source url is required")
|
||||
}
|
||||
if src.Mode == SourceModeProxy {
|
||||
src.ProxyPath = "/api/rooms/" + r.Code + "/stream"
|
||||
}
|
||||
r.Source = &src
|
||||
r.Playback = Playback{State: PlaybackIdle, ServerUpdatedAt: NowMS()}
|
||||
if err := s.store.SaveRoom(ctx, r); err != nil {
|
||||
return Room{}, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdatePlayback(ctx context.Context, code string, p Playback) (Room, error) {
|
||||
r, err := s.Get(ctx, code)
|
||||
if err != nil {
|
||||
return Room{}, err
|
||||
}
|
||||
p.ServerUpdatedAt = NowMS()
|
||||
r.Playback = p
|
||||
if err := s.store.SaveRoom(ctx, r); err != nil {
|
||||
return Room{}, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func randomCode(n int) (string, error) {
|
||||
var b strings.Builder
|
||||
b.Grow(n)
|
||||
for i := 0; i < n; i++ {
|
||||
x, err := rand.Int(rand.Reader, big.NewInt(int64(len(alphabet))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b.WriteByte(alphabet[x.Int64()])
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
||||
Reference in New Issue
Block a user