12 KiB
Multi-Event Invitation Management System - Refactoring Guide
Overview
The wedding guest list application has been refactored from a single-event system to a multi-event architecture that can manage invitations for multiple events (weddings, parties, conferences, etc.).
Key Architectural Changes
Database Schema (PostgreSQL)
New Tables:
-
users - User accounts (organizers/event managers)
id (UUID PK) | email (unique) | created_at -
events - Individual events
id (UUID PK) | name | date | location | created_at | updated_at -
event_members - User membership in events with roles
id (UUID PK) | event_id (FK) | user_id (FK) | role | display_name | created_at - Roles: admin, editor, viewer - UNIQUE constraint on (event_id, user_id) -
guests_v2 - Guest information (scoped by event, NO separate table per event)
id (UUID PK) | event_id (FK) | added_by_user_id (FK) | first_name | last_name | phone | side | status | notes | created_at | updated_at - Status: invited, confirmed, declined - Indexed: (event_id), (event_id, added_by_user_id), (event_id, phone)
Database Migration
Run the SQL migration to create new tables:
psql -U wedding_admin -d wedding_guests -f backend/migrations.sql
The migration includes a commented-out data migration script that can import existing data to a default event.
Backend Changes (FastAPI)
New Core Modules
1. models.py - SQLAlchemy Models
User- User accounts with relationshipsEvent- Event details with cascade deleteEventMember- Role-based event membershipGuest- Guest entries (links to events with added_by_user)- Uses UUID primary keys throughout
- Uses SQLAlchemy enums for roles and status
2. schemas.py - Pydantic Validation Models
- Organized into sections: User, Event, EventMember, Guest, WhatsApp
- Clear separation between Create/Update/Read schemas
- Type-safe with UUID and enum validation
3. crud.py - Database Operations
Reorganized into logical groups:
- User CRUD:
get_or_create_user(),get_user_by_email() - Event CRUD:
create_event(),get_events_for_user(), etc. - Event Member CRUD:
create_event_member(),get_event_member(), etc. - Guest CRUD (Event-scoped): All operations now take
event_idparameter - Statistics:
get_event_stats(),get_sides_summary()
4. authz.py - Authorization (NEW)
Role-based access control with permission checks:
class Permission:
can_edit_event(role) # admin only
can_manage_members(role) # admin only
can_add_guests(role) # editor+
can_send_messages(role) # all members
5. whatsapp.py - WhatsApp Integration (NEW)
- Phone number normalization to E.164 format
send_text_message()- Send direct messagessend_template_message()- Send approved templatesverify_webhook_signature()- Validate Meta webhooks- Error handling with custom
WhatsAppError
API Endpoints
Event Management
POST /events Create event (user becomes admin)
GET /events List user's events
GET /events/{event_id} Get event details
PATCH /events/{event_id} Update event (admin only)
DELETE /events/{event_id} Delete event (admin only)
Event Members
GET /events/{event_id}/members List members
POST /events/{event_id}/invite-member Invite by email (admin only)
PATCH /events/{event_id}/members/{user_id} Update role (admin only)
DELETE /events/{event_id}/members/{user_id} Remove member (admin only)
Guests (Event-Scoped)
POST /events/{event_id}/guests Add single guest
GET /events/{event_id}/guests List guests (with filters)
GET /events/{event_id}/guests/{guest_id} Get guest details
PATCH /events/{event_id}/guests/{guest_id} Update guest
DELETE /events/{event_id}/guests/{guest_id} Delete guest (admin only)
Bulk Operations
POST /events/{event_id}/guests/import Import multiple guests
POST /events/{event_id}/whatsapp Send message to guest
POST /events/{event_id}/whatsapp/broadcast Send to multiple guests
GET /events/{event_id}/stats Get event statistics
Authorization
All event-scoped endpoints enforce authorization:
- User must be a member of the event
- Permissions based on role:
- admin: Full control (create, delete, manage members)
- editor: Add/edit guests, import
- viewer: View only, can send messages
Implemented via:
verify_event_access()- Check membershipverify_event_admin()- Check admin roleverify_event_editor()- Check editor+ role
Frontend Changes (React/Vite)
New Components
1. EventList.jsx - Event Discovery
- Shows all events user is member of
- Quick stats: total guests, confirmation rate
- Create/delete event actions
- Card-based responsive layout
2. EventForm.jsx - Event Creation
- Modal form for new events
- Fields: name (required), date, location
- Automatically adds creator as admin
3. EventMembers.jsx - Member Management
- Invite members by email
- Set member roles (admin/editor/viewer)
- Remove members
- Modal interface
Updated Components
App.jsx - Main Navigation
- New page states: 'events', 'guests', 'guest-self-service'
- Event selection flow: List → Detail → Guests
- Modal overlays for forms
api/api.js - Event-Scoped Endpoints
- Reorganized into sections
- All guest operations now scoped by event
- New functions for events and members
- Backward compatibility where possible
Updated API Functions (examples)
// Events
getEvents() // List user's events
createEvent(event) // Create new event
getEventStats(eventId) // Get statistics
// Members
getEventMembers(eventId)
inviteEventMember(eventId, invite)
updateMemberRole(eventId, userId, role)
// Guests (now scoped)
getGuests(eventId, options) // List with filters
createGuest(eventId, guest) // Add single
bulkImportGuests(eventId, guests) // Bulk add
updateGuest(eventId, guestId, data) // Update
// WhatsApp
sendWhatsAppMessage(eventId, guestId, message)
broadcastWhatsAppMessage(eventId, request)
Environment Configuration
New Variables (.env)
# WhatsApp Cloud API (required for messaging)
WHATSAPP_ACCESS_TOKEN=...
WHATSAPP_PHONE_NUMBER_ID=...
WHATSAPP_API_VERSION=v20.0
WHATSAPP_VERIFY_TOKEN=... (optional, for webhooks)
# Test user (temporary - implement real auth)
TEST_USER_EMAIL=test@example.com
See .env.example for full template.
Migration Checklist
- Back up existing database
- Run
migrations.sqlto create new tables - Update backend dependencies (if any new ones added)
- Update frontend packages (axios already included)
- Test authentication (currently uses TEST_USER_EMAIL)
- Configure WhatsApp credentials (optional)
- Update FRONTEND_URL in .env for CORS
- Test event creation workflow
- Test member invitation
- Test guest management
Breaking Changes
Database
- Old
gueststable still exists but unused - Can be deleted after confirming data migration was successful:
DROP TABLE guests;
APIs
Old endpoints NO LONGER AVAILABLE:
GET /guests/→ UseGET /events/{event_id}/guestsPOST /guests/→ UsePOST /events/{event_id}/guestsGET /guests/{id}→ UseGET /events/{event_id}/guests/{id}
Frontend
- Old single-guest-list view replaced with event-first navigation
- Google import and duplicate manager need updates for event-scoped guests
Authentication (TODO)
Current implementation uses TEST_USER_EMAIL from .env.
Recommended approaches to implement:
- JWT Tokens - Extract user from Authorization header
- Session Cookies - HTTP-only cookies with session ID
- OAuth2 - Google/GitHub integration
- API Keys - For programmatic access
Update get_current_user_id() in main.py with your auth logic.
WhatsApp Integration
Setup Steps
- Create Meta Business App: https://developers.facebook.com/
- Add WhatsApp product
- Create test phone number or configure production
- Get credentials:
WHATSAPP_ACCESS_TOKEN- Long-lived tokenWHATSAPP_PHONE_NUMBER_ID- Phone number sender ID
- Add to
.envand.gitignore
Features
- Send Text Messages: Direct messages to guest phone (E.164 format)
- Bulk Broadcast: Send to multiple guests with optional filters
- Phone Validation: Automatic normalization (handles various formats)
- Error Handling: Detailed WhatsApp API non-200 errors
Usage Example
service = get_whatsapp_service()
result = await service.send_text_message(
to_phone="+972541234567",
message_text="Hello! Please confirm your attendance..."
)
File Structure
backend/
├── main.py # FastAPI app with all routes
├── models.py # SQLAlchemy ORM models (UPDATED)
├── schemas.py # Pydantic request/response schemas (UPDATED)
├── crud.py # Database operations (COMPLETELY REWRITTEN)
├── authz.py # Authorization & permissions (NEW)
├── whatsapp.py # WhatsApp API client (NEW)
├── database.py # DB connection setup
├── migrations.sql # SQL schema with new tables (NEW)
└── .env.example # Environment template (UPDATED)
frontend/src/
├── components/
│ ├── EventList.jsx # List/manage events (NEW)
│ ├── EventForm.jsx # Create event modal (NEW)
│ ├── EventMembers.jsx # Manage members (NEW)
│ ├── GuestList.jsx # Guest list (needs update for event scope)
│ └── ...
├── api/
│ └── api.js # API client (UPDATED)
└── App.jsx # Main app (UPDATED for events)
Performance Considerations
-
Indexes on guests_v2 for common queries:
event_id- Filter by event(event_id, status)- Filter by status(event_id, phone)- Lookup by phone
-
Pagination: List endpoints support skip/limit
-
Cascading Deletes: Deleting event removes all guests and memberships
Security Notes
- Authorization: Every event endpoint checks membership
- Phone Numbers: Validated/normalized before WhatsApp sends
- Secrets: Store ACCESS_TOKEN in .env, never commit
- CORS: Restricted to FRONTEND_URL (.env configuration)
- Roles: Implement fine-grained permissions (admin/editor/viewer)
Testing Recommendations
# Test event creation
psql -U wedding_admin -d wedding_guests -c "SELECT * FROM events;"
# Test member management
psql -U wedding_admin -d wedding_guests -c "SELECT * FROM event_members;"
# Test guest entries
psql -U wedding_admin -d wedding_guests -c "SELECT * FROM guests_v2 LIMIT 5;"
# Test API
curl http://localhost:8000/events
Next Steps
- Implement Real Authentication - Replace TEST_USER_EMAIL
- Add Google Import - Update for event-scoped guests
- Implement Self-Service Guest Updates - via token link
- Handle Webhooks - WhatsApp status callbacks
- Add Email Notifications - Event/RSVP confirmations
- Deploy Helm Charts - Uses new schema structure
Support
For issues or questions:
- Check
.envconfiguration - Review database indexes in
migrations.sql - Check authorization checks in
authz.py - Verify API response schemas in
schemas.py