# Band Invitation System - Complete Project Summary ## 1. User's Primary Goals and Intent ### Initial Request - **"Make a new branch, we're start working on the band invitation system"** - **"Evaluate the current system, and make a deep dive in all functions involved. then plan the new system."** ### Core Requirements 1. ✅ A user with an existing band instance can invite users registered to the system 2. ✅ Invited users are added to the band 3. ✅ No link handling needed (token-based system, no email notifications) 4. ✅ The user with the band instance is the admin (can add/remove members) ### Additional Clarifications - **"the mvp should be able to invite new members to a band without sending an existing user a link"** - Focus on token-based invite system (no email notifications) - Admin should be able to manage invites (list, revoke) ## 2. Conversation Timeline and Progress ### Phase 0: Analysis & Planning - **Action**: Created comprehensive analysis documents - **Files**: `BAND_INVITATION_ANALYSIS.md`, `IMPLEMENTATION_PLAN.md` - **Outcome**: Identified gaps in current system (no invite listing, no revocation, no user search) ### Phase 1: Backend Implementation - **Action**: Implemented 3 new API endpoints - **Files**: 7 files modified, 423 lines added - **Outcome**: Backend APIs for listing, revoking, and getting invite info - **Tests**: 13 integration tests written ### Phase 2: Frontend Implementation - **Action**: Created React components for invite management - **Files**: 5 files created/modified, 610 lines added - **Outcome**: InviteManagement component integrated into BandPage ### Phase 3: TypeScript Error Resolution - **Action**: Fixed all build errors - **Files**: 4 files modified, 16 lines removed - **Outcome**: All TypeScript errors resolved (TS6133, TS2304, TS2307) ### Current State - ✅ Backend: 3 endpoints implemented and tested - ✅ Frontend: InviteManagement component working - ✅ Build: All TypeScript errors resolved - ⏸️ UserSearch: Temporarily disabled (needs backend support) ## 3. Technical Context and Decisions ### Technologies - **Backend**: FastAPI, SQLAlchemy, PostgreSQL, Python 3.11+ - **Frontend**: React 18, TypeScript, TanStack Query, Vite - **Testing**: pytest, integration tests - **Deployment**: Docker, Podman Compose ### Architectural Decisions - **Token-based invites**: 72-hour expiry, random tokens (32 bytes) - **Permission model**: Only band admins can manage invites - **Repository pattern**: All DB access through BandRepository - **Service layer**: BandService handles business logic - **Pydantic v2**: Response schemas with from_attributes=True ### Key Constraints - No email notifications (requirement: "no link handling needed") - Existing JWT authentication system - Must work with existing Nextcloud integration - Follow existing code patterns and conventions ### Code Patterns ```python # Backend pattern @router.get("/{band_id}/invites", response_model=BandInviteList) async def list_invites(band_id: uuid.UUID, ...): # Check admin permissions # Get invites from repo # Return response ``` ```typescript // Frontend pattern const { data, isLoading } = useQuery({ queryKey: ["invites", bandId], queryFn: () => listInvites(bandId), }); ``` ## 4. Files and Code Changes ### Backend Files #### `api/src/rehearsalhub/routers/invites.py` (NEW) - **Purpose**: Invite management endpoints - **Key code**: ```python @router.get("/{token}/info", response_model=InviteInfoRead) async def get_invite_info(token: str, session: AsyncSession = Depends(get_session)): """Get invite details (public endpoint)""" repo = BandRepository(session) invite = await repo.get_invite_by_token(token) # Validate and return invite info ``` #### `api/src/rehearsalhub/routers/bands.py` (MODIFIED) - **Purpose**: Enhanced with invite listing and revocation - **Key additions**: ```python @router.get("/{band_id}/invites", response_model=BandInviteList) async def list_invites(band_id: uuid.UUID, ...): # Admin-only endpoint to list invites @router.delete("/invites/{invite_id}", status_code=status.HTTP_204_NO_CONTENT) async def revoke_invite(invite_id: uuid.UUID, ...): # Admin-only endpoint to revoke invites ``` #### `api/src/rehearsalhub/repositories/band.py` (MODIFIED) - **Purpose**: Added invite lookup methods - **Key additions**: ```python async def get_invites_for_band(self, band_id: uuid.UUID) -> list[BandInvite]: """Get all invites for a specific band.""" stmt = select(BandInvite).where(BandInvite.band_id == band_id) result = await self.session.execute(stmt) return list(result.scalars().all()) async def get_invite_by_id(self, invite_id: uuid.UUID) -> BandInvite | None: """Get invite by ID.""" stmt = select(BandInvite).where(BandInvite.id == invite_id) result = await self.session.execute(stmt) return result.scalar_one_or_none() ``` #### `api/src/rehearsalhub/schemas/invite.py` (MODIFIED) - **Purpose**: Added response schemas - **Key additions**: ```python class BandInviteListItem(BaseModel): """Invite for listing (includes creator info)""" id: uuid.UUID band_id: uuid.UUID token: str role: str expires_at: datetime created_at: datetime is_used: bool used_at: datetime | None = None class BandInviteList(BaseModel): """Response for listing invites""" invites: list[BandInviteListItem] total: int pending: int class InviteInfoRead(BaseModel): """Public invite info (used for /invites/{token}/info)""" id: uuid.UUID band_id: uuid.UUID band_name: str band_slug: str role: str expires_at: datetime created_at: datetime is_used: bool ``` #### `api/tests/integration/test_api_invites.py` (NEW) - **Purpose**: Integration tests for all 3 endpoints - **Key tests**: ```python @pytest.mark.asyncio async def test_list_invites_admin_can_see(client, db_session, auth_headers_for, band_with_admin): """Test that admin can list invites for their band.""" @pytest.mark.asyncio async def test_revoke_invite_admin_can_revoke(client, db_session, auth_headers_for, band_with_admin): """Test that admin can revoke an invite.""" @pytest.mark.asyncio async def test_get_invite_info_valid_token(client, db_session): """Test getting invite info with valid token.""" ``` ### Frontend Files #### `web/src/types/invite.ts` (NEW) - **Purpose**: TypeScript interfaces for invite data - **Key interfaces**: ```typescript export interface BandInviteListItem { id: string; band_id: string; token: string; role: string; expires_at: string; created_at: string; is_used: boolean; used_at: string | null; } export interface BandInviteList { invites: BandInviteListItem[]; total: number; pending: number; } export interface InviteInfo { id: string; band_id: string; band_name: string; band_slug: string; role: string; expires_at: string; created_at: string; is_used: boolean; } ``` #### `web/src/api/invites.ts` (NEW) - **Purpose**: API wrapper functions - **Key functions**: ```typescript export const listInvites = (bandId: string) => { return api.get(`/bands/${bandId}/invites`); }; export const revokeInvite = (inviteId: string) => { return api.delete(`/invites/${inviteId}`); }; export const getInviteInfo = (token: string) => { return api.get(`/invites/${token}/info`); }; ``` #### `web/src/components/InviteManagement.tsx` (NEW) - **Purpose**: Admin UI for managing invites - **Key features**: - List all pending invites - Revoke invites - Copy invite links to clipboard - Show invite status (pending/expired/used) - **Current state**: Clean, no unused code, all TypeScript errors resolved #### `web/src/pages/BandPage.tsx` (MODIFIED) - **Purpose**: Integrated InviteManagement component - **Key changes**: - Added import: `import { InviteManagement } from "../components/InviteManagement";` - Added component: `{amAdmin && }` - Removed UserSearch (temporarily disabled) ## 5. Active Work and Last Actions ### Most Recent Work - **Task**: Fixing TypeScript build errors - **Last action**: Removed unused `useState` import and `isRefreshing` reference - **Files modified**: - `web/src/components/InviteManagement.tsx`: Removed unused imports and variables - `web/src/api/invites.ts`: Removed unused parameters from `listNonMemberUsers` ### Current State - ✅ All TypeScript errors resolved - ✅ Build passing (no TS6133, TS2304, TS2307 errors) - ✅ Backend APIs working and tested - ✅ Frontend components integrated - ⏸️ UserSearch disabled (needs backend support) ### Recent Code Changes ```typescript // Before (with errors) import React, { useState } from "react"; // ... disabled={revokeMutation.isPending || isRefreshing} // After (fixed) import React from "react"; // ... disabled={revokeMutation.isPending} ``` ## 6. Unresolved Issues and Pending Tasks ### Current Issues - **Audio-worker build issue**: `podman_compose:Build command failed` (not related to our changes) - **403 errors in frontend**: Invited users getting 403 on `/bands/{id}/invites` and `/versions/{id}/stream` ### Pending Tasks 1. **UserSearch component**: Needs backend endpoint `GET /bands/{band_id}/non-members` 2. **Direct user invite**: Needs backend support for inviting specific users 3. **Email notifications**: Optional feature for future phase 4. **Invite analytics**: Track acceptance rates, etc. ### Decisions Waiting - Should we implement UserSearch backend endpoint? - Should we add email notification system? - Should we deploy current MVP to staging? ## 7. Immediate Next Step ### Priority: Resolve 403 Errors The user reported: ``` GET /api/v1/bands/96c11cfa-d6bb-4987-af80-845626880383/invites 403 (Forbidden) GET /api/v1/versions/973d000c-2ca8-4f02-8359-97646cf59086/stream 403 (Forbidden) ``` **Action**: Investigate permission issues for invited users - Check if invited users are properly added to band_members table - Verify JWT permissions for band access - Review backend permission checks in bands.py and versions.py ### Specific Task ```bash # 1. Check if invited user is in band_members SELECT * FROM band_members WHERE band_id = '96c11cfa-d6bb-4987-af80-845626880383'; # 2. Check invite acceptance flow SELECT * FROM band_invites WHERE band_id = '96c11cfa-d6bb-4987-af80-845626880383'; # 3. Review permission logic in: # - api/src/rehearsalhub/routers/bands.py # - api/src/rehearsalhub/routers/versions.py ``` The next step is to diagnose why invited users are getting 403 errors when accessing band resources and audio streams.