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