package service import ( "errors" "fmt" "math/rand/v2" "time" "filefast/backend/internal/model" "filefast/backend/internal/store" ) type RoomService struct { store *store.MemoryStore ttl time.Duration } func NewRoomService(store *store.MemoryStore, ttl time.Duration) *RoomService { return &RoomService{ store: store, ttl: ttl, } } func (s *RoomService) CreateRoom(creatorDeviceID string) (model.Room, error) { if creatorDeviceID == "" { return model.Room{}, errors.New("creator_device_id is required") } now := time.Now() for range 20 { code := fmt.Sprintf("%04d", rand.IntN(10000)) if room, ok := s.store.GetRoom(code); ok && room.ExpiresAt.After(now) && room.Status != model.RoomStatusExpired { continue } room := model.Room{ Code: code, CreatorDeviceID: creatorDeviceID, Status: model.RoomStatusWaiting, CreatedAt: now, ExpiresAt: now.Add(s.ttl), } return s.store.UpsertRoom(room), nil } return model.Room{}, errors.New("failed to allocate room code") } func (s *RoomService) JoinRoom(code, joinerDeviceID string) (model.Room, error) { room, ok := s.store.GetRoom(code) if !ok { return model.Room{}, errors.New("room not found") } now := time.Now() if !room.ExpiresAt.After(now) { room.Status = model.RoomStatusExpired s.store.UpsertRoom(room) return model.Room{}, errors.New("room expired") } if room.Status != model.RoomStatusWaiting { return model.Room{}, errors.New("room unavailable") } room.JoinerDeviceID = joinerDeviceID room.Status = model.RoomStatusJoined return s.store.UpsertRoom(room), nil } func (s *RoomService) CancelRoom(code, requesterID string) (model.Room, error) { room, ok := s.store.GetRoom(code) if !ok { return model.Room{}, errors.New("room not found") } if room.CreatorDeviceID != requesterID { return model.Room{}, errors.New("only creator can cancel room") } room.Status = model.RoomStatusCanceled return s.store.UpsertRoom(room), nil }