Files
membership-be/README.md
Koncept Kit db13f0e9de - Profile Picture\
Donation Tracking\
Validation Rejection\
Subscription Data Export\
Admin Dashboard Logo\
Admin Navbar Reorganization
2025-12-18 17:04:00 +07:00

601 lines
15 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <20> Developers <20> 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 <20> pending_validation <20> pre_validated <20> payment_pending <20> active
<20>
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 <jwt_token>
```
### 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 <20> 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