Files
sasa-membership/README.md

485 lines
18 KiB
Markdown

# Swansea Airport Stakeholders' Alliance Membership Management System
A membership management system for Swansea Airport Stakeholders' Alliance, built with FastAPI, React, MySQL-compatible storage, Square payments, SMTP2GO email services, and Docker Compose.
## Features
- **Authentication and accounts**: Registration, JSON/form login, JWT sessions, password reset, password change, and role-based access for members, admins, and super admins.
- **Member portal**: Dashboard with membership status, payment history, membership setup, account settings, profile editing, configurable profile questions, cookie notice, privacy policy, and terms of service pages.
- **Admin operations**: User listing/editing, admin-triggered member password reset emails, membership tier CRUD, manual payment recording, Square refunds, email template editing with escaped previews, SMTP2GO bounce management, profile-question management, and super-admin feature-flag reloads.
- **Membership tiers**: Configurable Personal, Aircraft Owners, Corporate, and custom tiers with annual fees, descriptions, active/inactive state, and benefits.
- **Memberships and payments**: Membership lifecycle tracking, Square card payments, cash/check/manual payments, dummy test payments, payment history, transaction IDs, refund state, and payment-to-membership linking.
- **Events and RSVPs**: Event CRUD, upcoming event listing, member RSVP updates, RSVP status tracking, attendance fields, and admin RSVP visibility.
- **Time handling**: Backend timestamps are stored and returned as UTC/Zulu, the frontend renders member-facing dates and times in Europe/London, and event entry is converted back to UTC before save.
- **ESP RFID**: Reader provisioning, heartbeat, time sync, tap capture, queued card writes, and admin review of readers/cards/attendance.
- **Volunteer and profile data**: Volunteer flag/level support, configurable member profile questions, conditional questions, admin-only answers, seeded aviation/volunteering questions, and data models for volunteer roles, assignments, schedules, and certificates.
- **Email system**: SMTP2GO-backed email sending, default database templates, editable templates, welcome/password-reset/test emails, bounce webhooks, bounce stats, cleanup, and manual deactivation.
- **Feature flags**: Backend feature-flag service with frontend context and admin status/reload controls.
- **Testing**: Fast frontend Vitest unit tests and backend pytest unit tests wired into `restart.sh`.
## Tech Stack
- **Backend**: FastAPI (Python 3.11)
- **Frontend**: React 18, TypeScript, Vite, Tailwind CSS
- **Database**: MySQL 8.0
- **Authentication**: JWT tokens with OAuth2
- **Containerization**: Docker & Docker Compose
- **ORM**: SQLAlchemy
- **Migrations**: Alembic
- **Payments**: Square Web Payments SDK and Square API
- **Email**: SMTP2GO
- **Tests**: Vitest and pytest
## Project Structure
```
membership/
├── backend/
│ ├── alembic/ # Database migration scripts
│ │ ├── versions/ # Migration files
│ │ ├── env.py # Migration environment
│ │ └── script.py.mako # Migration template
│ ├── alembic.ini # Alembic configuration
│ ├── app/
│ │ ├── api/
│ │ │ ├── v1/
│ │ │ │ ├── auth.py # Authentication endpoints
│ │ │ │ ├── users.py # User management
│ │ │ │ ├── tiers.py # Membership tiers
│ │ │ │ ├── memberships.py # Membership management
│ │ │ │ ├── payments.py # Payment processing
│ │ │ │ ├── email.py # SMTP2GO email and bounces
│ │ │ │ ├── email_templates.py
│ │ │ │ ├── events.py # Events and RSVPs
│ │ │ │ ├── esp.py # ESP RFID provisioning, taps, attendance, write jobs
│ │ │ │ └── feature_flags.py
│ │ │ └── dependencies.py # Auth dependencies
│ │ ├── core/
│ │ │ ├── config.py # Configuration
│ │ │ ├── database.py # Database setup
│ │ │ ├── datetime.py # UTC helpers and Zulu serialization helpers
│ │ │ └── security.py # Security utilities
│ │ ├── models/
│ │ │ └── models.py # Database models
│ │ ├── schemas/
│ │ │ └── schemas.py # Pydantic schemas
│ │ └── main.py # Application entry point
│ ├── Dockerfile
│ └── requirements.txt
├── frontend/
│ ├── src/
│ │ ├── components/ # Dashboard, payment, admin, profile, ESP components
│ │ ├── contexts/ # Feature flag, toast, and confirm contexts
│ │ ├── pages/ # Login, register, dashboard, policy pages
│ │ ├── services/ # API clients
│ │ └── utils/ # Tested frontend logic and timezone helpers
├── docker-compose.yml
├── .env.example
└── README.md
```
## Getting Started
### Prerequisites
- Docker
- Docker Compose
### Installation
1. **Navigate to the project directory**
2. **The `.env` file is already configured** with default settings. You can customize:
- Square API credentials (for payment processing)
- SMTP2GO API key (for email notifications)
- Database password (if desired)
3. **Start the services**:
```bash
# Development mode (gateway + Vite hot reload)
docker compose up -d
# Production static frontend mode
docker compose --profile prod up -d frontend-prod backend
```
4. **Wait for services to be ready** (about 30 seconds for MySQL initialization):
```bash
docker compose logs -f
```
Press Ctrl+C when you see "Application startup complete"
5. **Access the application**:
- Frontend (HTTP): http://localhost:8050
- Frontend (HTTPS for Square): https://localhost:8443
- API: http://localhost:8050/api/v1
- API Documentation: http://localhost:8050/docs
- TLS certs are generated automatically by the gateway container on first start
## Restart and Test Gate
`restart.sh` rebuilds images with cache, runs the fast frontend and backend unit tests, then restarts the stack only if tests pass:
```bash
./restart.sh
```
The current fast test suite covers:
- frontend profile-question visibility and editability rules with Vitest
- backend profile-question option parsing, answer normalization/deserialization, select validation, and volunteer flag normalization with pytest
You can also run them individually:
```bash
docker compose run --rm frontend npm test
docker compose run --rm backend pytest -q
```
## Frontend Development vs Production
### Development Mode (Vite)
```bash
docker compose up -d
```
- Single entry point on port 8050
- Nginx gateway routes `/api/*` to backend and all other traffic to Vite
- Hot reloading and development features
- Access at: http://localhost:8050
### Production Mode (Nginx)
```bash
docker compose --profile prod up -d frontend-prod backend
```
- Frontend served by Nginx on port 8050
- Optimized static files, production-ready
- Access at: http://localhost:8050
### Configuration
Set your preferred defaults in `.env`:
```bash
# Deployment mode (for reference only)
MODE=dev
# Single host entrypoint port
APP_PORT=8050
# HTTPS entrypoint port for self-signed TLS in dev
APP_TLS_PORT=8443
DEV_CERT_CN=localhost
DEV_CERT_SANS=DNS:localhost,IP:127.0.0.1,IP:::1
# Frontend allowed hosts (comma-separated)
VITE_ALLOWED_HOSTS=sasaprod,localhost,members.sasalliance.org
```
If you access via a LAN hostname or IP, add it to `VITE_ALLOWED_HOSTS`.
If you change `APP_PORT`, use that port instead of `8050` in URLs.
If you change `APP_TLS_PORT`, use that port for HTTPS URLs.
If you change hostnames, update `DEV_CERT_CN` and `DEV_CERT_SANS` accordingly.
### Stopping Services
```bash
# Stop all services
docker compose down
# Stop production profile services
docker compose --profile prod down
```
## Default Credentials
**Admin Account**:
- Email: `admin@swanseaairport.org`
- Password: `admin123`
⚠️ **IMPORTANT**: Change the admin password immediately after first login!
## API Endpoints
### Authentication
- `POST /api/v1/auth/register` - Register new user
- `POST /api/v1/auth/login` - Login (OAuth2 form)
- `POST /api/v1/auth/login-json` - Login (JSON)
### Users
- `GET /api/v1/users/me` - Get current user profile
- `PUT /api/v1/users/me` - Update current user profile
- `GET /api/v1/users/` - List all users (admin)
- `GET /api/v1/users/{id}` - Get user by ID (admin)
- `DELETE /api/v1/users/{id}` - Delete user (admin)
### Membership Tiers
- `GET /api/v1/tiers/` - List all tiers
- `GET /api/v1/tiers/{id}` - Get tier by ID
- `POST /api/v1/tiers/` - Create tier (admin)
- `PUT /api/v1/tiers/{id}` - Update tier (admin)
- `DELETE /api/v1/tiers/{id}` - Delete tier (admin)
### Memberships
- `GET /api/v1/memberships/my-memberships` - Get current user's memberships
- `POST /api/v1/memberships/` - Create membership
- `GET /api/v1/memberships/{id}` - Get membership by ID
- `PUT /api/v1/memberships/{id}` - Update membership (admin)
- `GET /api/v1/memberships/` - List all memberships (admin)
- `DELETE /api/v1/memberships/{id}` - Delete membership (admin)
### Payments
- `GET /api/v1/payments/my-payments` - Get current user's payments
- `POST /api/v1/payments/` - Create payment
- `GET /api/v1/payments/{id}` - Get payment by ID
- `PUT /api/v1/payments/{id}` - Update payment (admin)
- `GET /api/v1/payments/` - List all payments (admin)
- `POST /api/v1/payments/manual-payment` - Record manual payment (admin)
- `GET /api/v1/payments/config/square` - Get frontend Square config
- `POST /api/v1/payments/square/process` - Process Square card payment
- `POST /api/v1/payments/square/refund` - Refund Square payment (admin)
### Profile Questions
- `GET /api/v1/users/me/profile-questions` - List active questions with current answers
- `PUT /api/v1/users/me/profile-answers` - Update editable answers
- `GET /api/v1/users/admin/profile-questions` - List all profile questions (admin)
- `POST /api/v1/users/admin/profile-questions` - Create profile question (admin)
- `PUT /api/v1/users/admin/profile-questions/{id}` - Update profile question (admin)
- `DELETE /api/v1/users/admin/profile-questions/{id}` - Deactivate profile question (admin)
- `GET /api/v1/users/admin/users/{id}/profile-answers` - View user answers (admin)
- `PUT /api/v1/users/admin/users/{id}/profile-answers` - Update user answers (admin)
### Events
- `GET /api/v1/events/` - List events
- `GET /api/v1/events/upcoming` - List upcoming events
- `POST /api/v1/events/` - Create event (admin)
- `PUT /api/v1/events/{id}` - Update event (admin)
- `DELETE /api/v1/events/{id}` - Delete event (admin)
- `GET /api/v1/events/{id}/rsvps` - List RSVPs (admin)
- `POST /api/v1/events/{id}/rsvp` - Create or update current user's RSVP
### Email and Feature Flags
- `POST /api/v1/email/test-email` - Send test email
- `POST /api/v1/email/test-welcome-email` - Send test welcome email
- `POST /api/v1/email/webhooks/smtp2go/bounce` - Receive SMTP2GO bounce webhook
- `GET /api/v1/email/bounces` - List bounces
- `GET /api/v1/email/bounces/stats` - Bounce statistics
- `GET /api/v1/email-templates/` - List templates
- `PUT /api/v1/email-templates/{template_key}` - Update template
- `GET /api/v1/feature-flags/flags` - List flags
- `POST /api/v1/feature-flags/flags/reload` - Reload flags (super admin)
### ESP RFID
- `POST /api/v1/esp/device/register` - Reader registration and one-time token issuance
- `GET /api/v1/esp/device/provisioning-status` - Poll reader provisioning
- `GET /api/v1/esp/device/time` - UTC clock sync for ESP firmware
- `POST /api/v1/esp/device/heartbeat` - Reader heartbeat with UTC server time
- `POST /api/v1/esp/device/taps` - RFID tap capture with UTC-normalized timestamps
- `GET /api/v1/esp/device/write-jobs/next` - Poll queued card write job
- `POST /api/v1/esp/device/write-jobs/{job_id}/complete` - Complete a queued write job
- `POST /api/v1/esp/device/dashboard-login` - Validate ESP-hosted dashboard login
- `GET /api/v1/esp/admin/readers` - Admin reader list
- `POST /api/v1/esp/admin/readers` - Admin reader create/provision fallback
- `PUT /api/v1/esp/admin/readers/{reader_id}` - Admin reader update / key rotation
- `POST /api/v1/esp/admin/readers/{reader_id}/approve` - Approve a reader
- `POST /api/v1/esp/admin/readers/{reader_id}/reject` - Reject a reader
- `DELETE /api/v1/esp/admin/readers/{reader_id}` - Delete a reader
- `GET /api/v1/esp/admin/cards` - Admin RFID card list
- `POST /api/v1/esp/admin/cards` - Create RFID card
- `PUT /api/v1/esp/admin/cards/{card_id}` - Update RFID card
- `GET /api/v1/esp/admin/write-jobs` - Admin queued write jobs
- `POST /api/v1/esp/admin/write-jobs` - Queue a write job
- `POST /api/v1/esp/admin/write-jobs/{job_id}/cancel` - Cancel a queued write job
- `GET /api/v1/esp/admin/taps` - Admin tap history
- `GET /api/v1/esp/admin/attendance` - Admin attendance sessions
- `POST /api/v1/esp/admin/attendance/close-stale` - Close stale attendance sessions
## Time Handling
- Database timestamps are stored as UTC and serialized as Zulu (`...Z`) in API responses.
- Frontend display uses Europe/London for member-facing dates and times.
- Event creation/editing converts London-local input back to UTC before sending it to the backend.
- ESP devices sync their clocks from `/api/v1/esp/device/time` and persist tap times as UTC.
## Docker Compose Commands
### Basic Operations
```bash
# Start all services
docker compose up -d
# View logs (all services)
docker compose logs -f
# View logs (specific service)
docker compose logs -f backend
docker compose logs -f gateway
# Stop services
docker compose down
# Stop and remove volumes (clean slate)
docker compose down -v
# Restart services
docker compose restart
# Rebuild after code changes
docker compose up -d --build
# Check service status
docker compose ps
```
### Database Operations
```bash
# Access MySQL CLI (using environment variables)
docker exec -it membership_mysql mysql -u "${DATABASE_USER}" -p"${DATABASE_PASSWORD}" "${DATABASE_NAME}"
# Create backup
docker exec membership_mysql mysqldump -u "${DATABASE_USER}" -p"${DATABASE_PASSWORD}" "${DATABASE_NAME}" > backup_$(date +%Y%m%d_%H%M%S).sql
# Restore database
docker exec -i membership_mysql mysql -u "${DATABASE_USER}" -p"${DATABASE_PASSWORD}" "${DATABASE_NAME}" < backup.sql
```
### Database Migrations
The application uses Alembic for database schema migrations. Migrations are automatically run when the backend container starts.
```bash
# Create a new migration (after making model changes)
sudo docker compose exec backend alembic revision --autogenerate -m "Description of changes"
# Apply migrations
sudo docker compose exec backend alembic upgrade head
# View migration status
sudo docker compose exec backend alembic current
# View migration history
sudo docker compose exec backend alembic history
```
**Note**: The `database/init.sql` file is deprecated. All schema changes should now be made through Alembic migrations.
## API Testing
You can use the interactive API documentation at http://localhost:8050/docs to test endpoints:
1. Register a new user
2. Login to get access token
3. Click "Authorize" button and enter: `Bearer <your_token>`
4. Test protected endpoints
Or use curl:
```bash
# Register
curl -X POST "http://localhost:8050/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "password123",
"first_name": "John",
"last_name": "Doe"
}'
# Login
curl -X POST "http://localhost:8050/api/v1/auth/login-json" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "password123"
}'
# Get profile (replace TOKEN with actual token)
curl -X GET "http://localhost:8050/api/v1/users/me" \
-H "Authorization: Bearer TOKEN"
```
## Default Membership Tiers
The system comes with three pre-configured tiers:
1. **Personal** - £5/year
- Access to member portal
- Meeting notifications
- Event participation
2. **Aircraft Owners** - £25/year
- All Personal benefits
- Priority event registration
- Aircraft owner resources
3. **Corporate** - £100/year
- All benefits
- Corporate recognition
- Promotional opportunities
- File access
## Security
- Passwords are hashed using bcrypt
- JWT tokens for authentication
- Role-based access control (Member, Admin, Super Admin)
- CORS protection
- Environment-based configuration
## Troubleshooting
### Services not starting
```bash
# Check status of all services
docker compose ps
# View all logs
docker compose logs
# View specific service logs
docker compose logs gateway
docker compose logs backend
# Restart all services
docker compose restart
# Full restart with rebuild
docker compose down
docker compose up -d --build
```
### Database connection issues
```bash
# Check backend connectivity and status
docker compose ps
# View backend logs for DB errors
docker compose logs backend
```
### Clean slate restart
```bash
# Stop everything and remove volumes
docker compose down -v
# Start fresh
docker compose up -d
# Wait for initialization
docker compose logs -f
```
## Remaining Roadmap
- [ ] Add member file upload/repository endpoints and UI
- [ ] Add richer volunteer role, assignment, schedule, and certificate screens on top of the existing models
- [ ] Implement automated renewal reminder batch jobs
- [ ] Add reporting and analytics
- [ ] Expand test coverage around authenticated API flows and payment/email service boundaries
## License
Copyright © 2024 Swansea Airport Stakeholders' Alliance
## Support
For issues or questions, please contact the development team.