init
This commit is contained in:
248
backend/app/routers/rooms.py
Normal file
248
backend/app/routers/rooms.py
Normal file
@@ -0,0 +1,248 @@
|
||||
from uuid import UUID
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
from sqlalchemy.orm import selectinload
|
||||
from ..database import get_db
|
||||
from ..models.user import User
|
||||
from ..models.room import Room, RoomParticipant
|
||||
from ..models.track import RoomQueue
|
||||
from ..schemas.room import RoomCreate, RoomResponse, RoomDetailResponse, QueueAdd
|
||||
from ..schemas.track import TrackResponse
|
||||
from ..schemas.user import UserResponse
|
||||
from ..services.auth import get_current_user
|
||||
from ..services.sync import manager
|
||||
from ..config import get_settings
|
||||
|
||||
settings = get_settings()
|
||||
router = APIRouter(prefix="/api/rooms", tags=["rooms"])
|
||||
|
||||
|
||||
@router.get("", response_model=list[RoomResponse])
|
||||
async def get_rooms(db: AsyncSession = Depends(get_db)):
|
||||
result = await db.execute(
|
||||
select(Room, func.count(RoomParticipant.user_id).label("participants_count"))
|
||||
.outerjoin(RoomParticipant)
|
||||
.group_by(Room.id)
|
||||
.order_by(Room.created_at.desc())
|
||||
)
|
||||
rooms = []
|
||||
for room, count in result.all():
|
||||
room_dict = {
|
||||
"id": room.id,
|
||||
"name": room.name,
|
||||
"owner_id": room.owner_id,
|
||||
"current_track_id": room.current_track_id,
|
||||
"playback_position": room.playback_position,
|
||||
"is_playing": room.is_playing,
|
||||
"created_at": room.created_at,
|
||||
"participants_count": count,
|
||||
}
|
||||
rooms.append(RoomResponse(**room_dict))
|
||||
return rooms
|
||||
|
||||
|
||||
@router.post("", response_model=RoomResponse)
|
||||
async def create_room(
|
||||
room_data: RoomCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
room = Room(name=room_data.name, owner_id=current_user.id)
|
||||
db.add(room)
|
||||
await db.flush()
|
||||
return RoomResponse(
|
||||
id=room.id,
|
||||
name=room.name,
|
||||
owner_id=room.owner_id,
|
||||
current_track_id=room.current_track_id,
|
||||
playback_position=room.playback_position,
|
||||
is_playing=room.is_playing,
|
||||
created_at=room.created_at,
|
||||
participants_count=0,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/{room_id}", response_model=RoomDetailResponse)
|
||||
async def get_room(room_id: UUID, db: AsyncSession = Depends(get_db)):
|
||||
result = await db.execute(
|
||||
select(Room)
|
||||
.options(
|
||||
selectinload(Room.owner),
|
||||
selectinload(Room.current_track),
|
||||
selectinload(Room.participants).selectinload(RoomParticipant.user),
|
||||
)
|
||||
.where(Room.id == room_id)
|
||||
)
|
||||
room = result.scalar_one_or_none()
|
||||
|
||||
if not room:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Room not found")
|
||||
|
||||
return RoomDetailResponse(
|
||||
id=room.id,
|
||||
name=room.name,
|
||||
owner=UserResponse.model_validate(room.owner),
|
||||
current_track=TrackResponse.model_validate(room.current_track) if room.current_track else None,
|
||||
playback_position=room.playback_position,
|
||||
is_playing=room.is_playing,
|
||||
created_at=room.created_at,
|
||||
participants=[UserResponse.model_validate(p.user) for p in room.participants],
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/{room_id}")
|
||||
async def delete_room(
|
||||
room_id: UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(select(Room).where(Room.id == room_id))
|
||||
room = result.scalar_one_or_none()
|
||||
|
||||
if not room:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Room not found")
|
||||
|
||||
if room.owner_id != current_user.id:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not room owner")
|
||||
|
||||
await db.delete(room)
|
||||
return {"status": "deleted"}
|
||||
|
||||
|
||||
@router.post("/{room_id}/join")
|
||||
async def join_room(
|
||||
room_id: UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(select(Room).where(Room.id == room_id))
|
||||
room = result.scalar_one_or_none()
|
||||
|
||||
if not room:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Room not found")
|
||||
|
||||
# Check participant limit
|
||||
result = await db.execute(
|
||||
select(func.count(RoomParticipant.user_id)).where(RoomParticipant.room_id == room_id)
|
||||
)
|
||||
count = result.scalar()
|
||||
if count >= settings.max_room_participants:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Room is full")
|
||||
|
||||
# Check if already joined
|
||||
result = await db.execute(
|
||||
select(RoomParticipant).where(
|
||||
RoomParticipant.room_id == room_id,
|
||||
RoomParticipant.user_id == current_user.id,
|
||||
)
|
||||
)
|
||||
if result.scalar_one_or_none():
|
||||
return {"status": "already joined"}
|
||||
|
||||
participant = RoomParticipant(room_id=room_id, user_id=current_user.id)
|
||||
db.add(participant)
|
||||
|
||||
# Notify others
|
||||
await manager.broadcast_to_room(
|
||||
room_id,
|
||||
{"type": "user_joined", "user": {"id": str(current_user.id), "username": current_user.username}},
|
||||
)
|
||||
|
||||
return {"status": "joined"}
|
||||
|
||||
|
||||
@router.post("/{room_id}/leave")
|
||||
async def leave_room(
|
||||
room_id: UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(
|
||||
select(RoomParticipant).where(
|
||||
RoomParticipant.room_id == room_id,
|
||||
RoomParticipant.user_id == current_user.id,
|
||||
)
|
||||
)
|
||||
participant = result.scalar_one_or_none()
|
||||
|
||||
if participant:
|
||||
await db.delete(participant)
|
||||
|
||||
# Notify others
|
||||
await manager.broadcast_to_room(
|
||||
room_id,
|
||||
{"type": "user_left", "user_id": str(current_user.id)},
|
||||
)
|
||||
|
||||
return {"status": "left"}
|
||||
|
||||
|
||||
@router.get("/{room_id}/queue", response_model=list[TrackResponse])
|
||||
async def get_queue(room_id: UUID, db: AsyncSession = Depends(get_db)):
|
||||
result = await db.execute(
|
||||
select(RoomQueue)
|
||||
.options(selectinload(RoomQueue.track))
|
||||
.where(RoomQueue.room_id == room_id)
|
||||
.order_by(RoomQueue.position)
|
||||
)
|
||||
queue_items = result.scalars().all()
|
||||
return [TrackResponse.model_validate(item.track) for item in queue_items]
|
||||
|
||||
|
||||
@router.post("/{room_id}/queue")
|
||||
async def add_to_queue(
|
||||
room_id: UUID,
|
||||
data: QueueAdd,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
# Get max position
|
||||
result = await db.execute(
|
||||
select(func.max(RoomQueue.position)).where(RoomQueue.room_id == room_id)
|
||||
)
|
||||
max_pos = result.scalar() or 0
|
||||
|
||||
queue_item = RoomQueue(
|
||||
room_id=room_id,
|
||||
track_id=data.track_id,
|
||||
position=max_pos + 1,
|
||||
added_by=current_user.id,
|
||||
)
|
||||
db.add(queue_item)
|
||||
await db.flush()
|
||||
|
||||
# Notify others
|
||||
await manager.broadcast_to_room(
|
||||
room_id,
|
||||
{"type": "queue_updated"},
|
||||
)
|
||||
|
||||
return {"status": "added"}
|
||||
|
||||
|
||||
@router.delete("/{room_id}/queue/{track_id}")
|
||||
async def remove_from_queue(
|
||||
room_id: UUID,
|
||||
track_id: UUID,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
result = await db.execute(
|
||||
select(RoomQueue).where(
|
||||
RoomQueue.room_id == room_id,
|
||||
RoomQueue.track_id == track_id,
|
||||
)
|
||||
)
|
||||
queue_item = result.scalar_one_or_none()
|
||||
|
||||
if queue_item:
|
||||
await db.delete(queue_item)
|
||||
|
||||
# Notify others
|
||||
await manager.broadcast_to_room(
|
||||
room_id,
|
||||
{"type": "queue_updated"},
|
||||
)
|
||||
|
||||
return {"status": "removed"}
|
||||
Reference in New Issue
Block a user