# LOAF Membership Platform - Backend API FastAPI-based backend service for the LOAF (LGBT Organization and Friends) membership management platform. ## Table of Contents - [Setup & Installation](#setup--installation) - [Architecture & Code Structure](#architecture--code-structure) - [API Documentation](#api-documentation) - [Deployment Guide](#deployment-guide) - [Troubleshooting](#troubleshooting) --- ## Setup & Installation ### Prerequisites - **Python**: 3.9 or higher - **PostgreSQL**: 13 or higher - **Cloudflare R2**: Account and credentials (for file storage) - **Stripe**: Account and API keys (for payments) - **SMTP Server**: For sending emails (Gmail, SendGrid, etc.) ### 1. Clone Repository ```bash cd backend ``` ### 2. Create Virtual Environment ```bash # Create virtual environment python -m venv venv # Activate virtual environment # On macOS/Linux: source venv/bin/activate # On Windows: venv\Scripts\activate ``` ### 3. Install Dependencies ```bash pip install -r requirements.txt ``` **Key Dependencies:** - `fastapi==0.110.1` - Web framework - `uvicorn==0.25.0` - ASGI server - `sqlalchemy==2.0.44` - ORM for database - `psycopg2-binary==2.9.10` - PostgreSQL adapter - `python-jose[cryptography]==3.5.0` - JWT tokens - `passlib[bcrypt]==1.7.4` - Password hashing - `aiosmtplib==5.0.0` - Async email sending - `boto3==1.35.95` - AWS SDK (for Cloudflare R2) - `stripe==11.4.0` - Payment processing - `python-multipart==0.0.18` - File uploads ### 4. Database Setup #### Create PostgreSQL Database ```bash # Connect to PostgreSQL psql -U postgres # Create database CREATE DATABASE membership_db; # Create user (optional) CREATE USER membership_user WITH PASSWORD 'your_password'; GRANT ALL PRIVILEGES ON DATABASE membership_db TO membership_user; # Exit psql \q ``` #### Run Migrations **⚠️ IMPORTANT:** For complete migration documentation, see **[MIGRATIONS.md](./MIGRATIONS.md)** **For Fresh Database (New Deployment):** ```bash # Run comprehensive initial schema (includes all base tables) psql -U postgres -d membership_db -f migrations/000_initial_schema.sql ``` This single migration creates: - All 10 ENUM types (userstatus, userrole, rsvpstatus, etc.) - All 17 base tables (users, events, subscriptions, donations, RBAC, etc.) - 30+ performance indexes - Default storage_usage record **For Existing Database (Incremental Updates):** If you already have a database and need to apply specific updates, see the migration sequence in [MIGRATIONS.md](./MIGRATIONS.md). **Next Steps After Migration:** ```bash # 1. Seed permissions (59 permissions across 10 modules) python seed_permissions_rbac.py # 2. Create superadmin user (interactive) python create_admin.py ``` ### 5. Environment Configuration Create `.env` file in the backend directory: ```bash # Database DATABASE_URL=postgresql://postgres:password@localhost:5432/membership_db # JWT Configuration JWT_SECRET=your-super-secret-key-change-in-production JWT_ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=30 # SMTP Email Configuration SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USERNAME=your-email@gmail.com SMTP_PASSWORD=your-app-password SMTP_FROM_EMAIL=noreply@loaf.org SMTP_FROM_NAME=LOAF Membership Platform # Frontend URL (for email links and CORS) FRONTEND_URL=http://localhost:3000 # Cloudflare R2 Storage R2_ACCOUNT_ID=your-cloudflare-account-id R2_ACCESS_KEY_ID=your-r2-access-key R2_SECRET_ACCESS_KEY=your-r2-secret-key R2_BUCKET_NAME=loaf-membership R2_PUBLIC_URL=https://your-r2-bucket.r2.dev # Stripe Payment Processing STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_publishable_key STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret # Server Configuration PORT=8000 HOST=0.0.0.0 CORS_ORIGINS=http://localhost:3000,http://localhost:3001 # Optional: Sentry (error tracking) SENTRY_DSN=your-sentry-dsn ``` **Important Notes:** - **Gmail SMTP**: Use App Password, not your regular password. Enable 2FA and generate app-specific password at https://myaccount.google.com/apppasswords - **JWT_SECRET**: Generate a strong random key: `openssl rand -hex 32` - **Stripe Webhook Secret**: Get from Stripe Dashboard � Developers � Webhooks ### 6. Seed Initial Data ```bash # Seed permissions (must be run after migrations) python seed_permissions_rbac.py # Create superadmin user (optional) python create_superadmin.py ``` ### 7. Start Development Server ```bash # Start with auto-reload uvicorn server:app --reload --host 0.0.0.0 --port 8000 # Or using the PORT from .env uvicorn server:app --reload --host 0.0.0.0 --port $PORT ``` **Server will be available at:** - API: http://localhost:8000 - Swagger UI: http://localhost:8000/docs - ReDoc: http://localhost:8000/redoc --- ## Architecture & Code Structure ### Project Structure ``` backend/  server.py # Main FastAPI app + all API routes (~4000 lines)  models.py # SQLAlchemy ORM models  auth.py # JWT utilities + password hashing  email_service.py # Async email sending with HTML templates  database.py # Database connection & session management  storage_service.py # Cloudflare R2 file upload/download  requirements.txt # Python dependencies  seed_permissions_rbac.py # RBAC permissions seeding script  migrations/ # Database migration SQL scripts   001_initial_schema.sql   009_create_donations.sql   010_add_rejection_fields.sql  .env # Environment variables (not in git) ``` ### Core Technologies | Technology | Purpose | Version | |------------|---------|---------| | FastAPI | Web framework | 0.110.1 | | Uvicorn | ASGI server | 0.25.0 | | SQLAlchemy | ORM for database | 2.0.44 | | PostgreSQL | Relational database | 13+ | | Python-JOSE | JWT token handling | 3.5.0 | | Bcrypt | Password hashing | via passlib | | Aiosmtplib | Async email sending | 5.0.0 | | Boto3 | Cloudflare R2 client | 1.35.95 | | Stripe | Payment processing | 11.4.0 | ### Key Data Models #### User Model **Status Flow:** ``` pending_email � pending_validation � pre_validated � payment_pending � active � rejected / inactive ``` **Key Fields:** - Authentication: email, password_hash - Profile: first_name, last_name, phone, bio, profile_photo_url - Status tracking: status, role, email_verified - Rejection: rejection_reason, rejected_at, rejected_by - Partner info: partner_first_name, partner_last_name, partner_is_member #### Event Model - Event management with RSVP tracking - Fields: title, description, location, start_at, end_at, capacity, published #### Subscription Model - Links users to subscription plans - Tracks payment status, billing cycle, donations - Fields: stripe_subscription_id, base_subscription_cents, donation_cents #### Donation Model - Separate tracking for standalone donations - Types: member donations vs public donations - Fields: amount_cents, donation_type, status, donor_email, donor_name #### RBAC Models - **Role**: Dynamic roles (admin, finance, superadmin) - **Permission**: Granular permissions (users.view, events.create, etc.) - **RolePermission**: Many-to-many relationship - **UserRoleAssignment**: User role assignments ### Authentication System **JWT Token Structure:** ```json { "sub": "user@example.com", "user_id": "uuid", "role": "member", "exp": 1234567890 } ``` **Password Security:** - Bcrypt hashing with automatic salt - Never store plaintext passwords - Token expiration: 30 minutes (configurable) ### Permission System (RBAC) **59 Granular Permissions** across 10 modules: - `users.*` - User management - `events.*` - Event management - `subscriptions.*` - Subscription management - `donations.*` - Donation tracking - `permissions.*` - RBAC administration **Example Usage:** ```python @api_router.get("/admin/users") async def get_users( current_user: User = Depends(require_permission("users.view")), db: Session = Depends(get_db) ): # Only users with 'users.view' permission can access ... ``` ### Email Service **5 Email Templates:** 1. Email verification 2. Approval notification 3. Rejection with reason 4. Donation thank you 5. Payment prompt All emails use branded HTML templates with LOAF color scheme. ### Storage Service (Cloudflare R2) - Profile photo uploads (max 50MB) - Event cover images - Gallery images - Automatic URL generation with CDN --- ## API Documentation ### Base URL ``` Development: http://localhost:8000 Production: https://api.loaf.org ``` ### Authentication Most endpoints require JWT token in Authorization header: ``` Authorization: Bearer ``` ### Interactive Docs - **Swagger UI**: http://localhost:8000/docs - **ReDoc**: http://localhost:8000/redoc ### Key Endpoint Groups #### Authentication (`/api/auth`) - `POST /auth/register` - User registration - `POST /auth/login` - Login with JWT token - `GET /auth/verify-email` - Email verification - `GET /auth/me` - Current user profile #### Member Profile (`/api/members`) - `GET /members/profile` - Get profile - `PUT /members/profile` - Update profile - `POST /members/profile/upload-photo` - Upload profile picture - `DELETE /members/profile/delete-photo` - Delete profile picture #### Events (`/api/events`) - `GET /events` - List published events - `GET /events/{id}` - Event details - `POST /events/{id}/rsvp` - Submit RSVP #### Admin Users (`/api/admin/users`) - `GET /admin/users` - List users (with filters) - `PUT /admin/users/{id}/approve` - Approve user - `POST /admin/users/{id}/reject` - Reject with reason - `GET /admin/users/export` - Export to CSV #### Admin Events (`/api/admin/events`) - `GET /admin/events` - List all events - `POST /admin/events` - Create event - `PUT /admin/events/{id}` - Update event - `PUT /admin/events/{id}/attendance` - Mark attendance #### Subscriptions (`/api/admin/subscriptions`) - `GET /admin/subscriptions` - List subscriptions - `GET /admin/subscriptions/stats` - Statistics - `GET /admin/subscriptions/export` - Export to CSV - `POST /admin/subscriptions/{id}/cancel` - Cancel subscription #### Donations (`/api/admin/donations`) - `GET /admin/donations` - List donations - `GET /admin/donations/stats` - Statistics - `GET /admin/donations/export` - Export to CSV #### Payments (`/api`) - `POST /checkout/subscription` - Create Stripe checkout - `POST /donations/checkout` - Create donation checkout - `POST /webhooks/stripe` - Handle Stripe webhooks ### Response Formats **Success:** ```json { "id": "uuid", "field": "value" } ``` **Error:** ```json { "detail": "Error message" } ``` **Status Codes:** - 200 - Success - 201 - Created - 400 - Bad Request - 401 - Unauthorized - 403 - Forbidden - 404 - Not Found - 500 - Server Error --- ## Deployment Guide ### Production Setup #### 1. Server Requirements - Ubuntu 20.04 LTS+ - Python 3.9+ - PostgreSQL 13+ - 2GB+ RAM - 20GB+ SSD #### 2. Install Dependencies ```bash sudo apt update && sudo apt upgrade -y sudo apt install python3.9 python3.9-venv python3-pip postgresql nginx -y ``` #### 3. Application Setup ```bash # Create directory sudo mkdir -p /var/www/loaf-backend cd /var/www/loaf-backend # Setup virtual environment python3.9 -m venv venv source venv/bin/activate pip install -r requirements.txt ``` #### 4. Configure Environment Create production `.env` with: - Strong JWT_SECRET (64+ chars) - Production database credentials - Production SMTP (SendGrid recommended) - Cloudflare R2 production credentials - Stripe LIVE API keys - HTTPS frontend URL #### 5. Database Setup ```bash # Create production database sudo -u postgres psql CREATE DATABASE membership_prod; CREATE USER membership_prod WITH ENCRYPTED PASSWORD 'strong_password'; GRANT ALL PRIVILEGES ON DATABASE membership_prod TO membership_prod; # Run migrations for file in migrations/*.sql; do psql postgresql://membership_prod:password@localhost/membership_prod -f "$file" done # Seed permissions python seed_permissions_rbac.py ``` #### 6. Systemd Service Create `/etc/systemd/system/loaf-backend.service`: ```ini [Unit] Description=LOAF Backend API After=network.target postgresql.service [Service] Type=notify User=www-data Group=www-data WorkingDirectory=/var/www/loaf-backend Environment="PATH=/var/www/loaf-backend/venv/bin" ExecStart=/var/www/loaf-backend/venv/bin/uvicorn server:app --host 0.0.0.0 --port 8000 --workers 4 Restart=always [Install] WantedBy=multi-user.target ``` Start service: ```bash sudo systemctl enable loaf-backend sudo systemctl start loaf-backend ``` #### 7. Nginx Reverse Proxy ```nginx server { listen 80; server_name api.loaf.org; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } ``` #### 8. SSL Certificate ```bash sudo apt install certbot python3-certbot-nginx -y sudo certbot --nginx -d api.loaf.org ``` #### 9. Configure Stripe Webhook - Stripe Dashboard � Webhooks - Add endpoint: `https://api.loaf.org/api/webhooks/stripe` - Events: checkout.session.completed, invoice.payment_succeeded - Copy webhook secret to .env ### Monitoring ```bash # View logs sudo journalctl -u loaf-backend -f # Database backup (daily cron) pg_dump membership_prod | gzip > backup_$(date +%Y%m%d).sql.gz ``` ### Security Checklist - [ ] Strong passwords and JWT secret - [ ] HTTPS with valid SSL - [ ] Firewall configured - [ ] SSH key authentication only - [ ] Environment variables secured - [ ] CORS restricted to production domain - [ ] Stripe webhook verification enabled - [ ] Regular backups configured - [ ] Error tracking (Sentry) enabled --- ## Troubleshooting ### Database Connection Error ```bash # Check PostgreSQL status sudo systemctl status postgresql # Verify DATABASE_URL format postgresql://user:password@host:port/database ``` ### Email Not Sending - Gmail: Use App Password (not regular password) - Enable 2FA first - Generate App Password at https://myaccount.google.com/apppasswords ### JWT Token Invalid - Check JWT_SECRET is consistent - Token expires after 30 minutes - User needs to log in again ### Stripe Webhook Failing - Verify endpoint is publicly accessible - Check STRIPE_WEBHOOK_SECRET matches dashboard - Review webhook logs in Stripe dashboard ### File Upload Fails - Check R2 credentials in .env - Verify bucket exists and has write permissions - Test connection with AWS CLI --- ## Additional Resources - **FastAPI Docs**: https://fastapi.tiangolo.com/ - **Stripe API**: https://stripe.com/docs/api - **Cloudflare R2**: https://developers.cloudflare.com/r2/ - **Project Context**: See `CLAUDE.md` and `PRD.md` --- **Last Updated**: December 18, 2024 **Version**: 1.0.0 **Maintainer**: LOAF Development Team