Phase 2: private rooms, room roles, and invites (backend only)

Adds room_invites table + migration, owner/admin/member role enforcement
(require_room_role), and endpoints for private room creation, room
management (update/delete/leave/transfer-ownership/change-role/remove-member),
and the invite lifecycle (create/list/accept/decline/revoke). Registration
stays invite-only via the CLI from Phase 1 — this is a separate, room-level
invite system for adding existing users to private rooms.

Frontend is untouched: the UI redesign is happening separately, so this phase
is backend + tests only (35 passing). Verified no regressions in the Phase 1
open-room/WebSocket flow via manual smoke test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 20:22:15 -06:00
co-authored by Claude Sonnet 5
parent 99aa029c0d
commit c79e96dd48
16 changed files with 1172 additions and 32 deletions
+12 -1
View File
@@ -5,7 +5,9 @@ from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.models import RoomMembership, User
from app.models import RoomMembership, RoomRole, User
_ROLE_RANK = {RoomRole.member: 0, RoomRole.admin: 1, RoomRole.owner: 2}
async def get_current_user(
@@ -35,3 +37,12 @@ async def require_room_member(
if membership is None:
raise HTTPException(status_code=403, detail="Not a member of this room")
return membership
async def require_room_role(
room_id: uuid.UUID, user: User, db: AsyncSession, minimum: RoomRole
) -> RoomMembership:
membership = await require_room_member(room_id, user, db)
if _ROLE_RANK[membership.role] < _ROLE_RANK[minimum]:
raise HTTPException(status_code=403, detail="Insufficient room role")
return membership