Files
rehearshalhub/COMPREHENSIVE_SUMMARY.md
Mistral Vibe e6fb64e161 Add comprehensive project summary document
- Complete summary of band invitation system implementation
- Captures all phases: analysis, backend, frontend, testing
- Documents technical decisions, file changes, and current state
- Includes unresolved issues and next steps

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 12:26:45 +02:00

10 KiB

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

# 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
// 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:
@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:
@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:
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:
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:
@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:
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:
export const listInvites = (bandId: string) => {
  return api.get<BandInviteList>(`/bands/${bandId}/invites`);
};

export const revokeInvite = (inviteId: string) => {
  return api.delete(`/invites/${inviteId}`);
};

export const getInviteInfo = (token: string) => {
  return api.get<InviteInfo>(`/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 && <InviteManagement bandId={bandId!} />}
    • 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

// 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

# 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.