Reviewed-on: #2
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.
- 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
│ │ │ │ └── feature_flags.py
│ │ │ └── dependencies.py # Auth dependencies
│ │ ├── core/
│ │ │ ├── config.py # Configuration
│ │ │ ├── database.py # Database setup
│ │ │ └── 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 components
│ │ ├── contexts/ # Feature flag context
│ │ ├── pages/ # Login, register, dashboard, policy pages
│ │ ├── services/ # API clients
│ │ └── utils/ # Tested frontend logic
├── docker-compose.yml
├── .env.example
└── README.md
Getting Started
Prerequisites
- Docker
- Docker Compose
Installation
-
Navigate to the project directory
-
The
.envfile 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)
-
Start the services:
# Development mode (gateway + Vite hot reload) docker compose up -d # Production static frontend mode docker compose --profile prod up -d frontend-prod backend -
Wait for services to be ready (about 30 seconds for MySQL initialization):
docker compose logs -fPress Ctrl+C when you see "Application startup complete"
-
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:
./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:
docker compose run --rm frontend npm test
docker compose run --rm backend pytest -q
Frontend Development vs Production
Development Mode (Vite)
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)
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:
# 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
# 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 userPOST /api/v1/auth/login- Login (OAuth2 form)POST /api/v1/auth/login-json- Login (JSON)
Users
GET /api/v1/users/me- Get current user profilePUT /api/v1/users/me- Update current user profileGET /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 tiersGET /api/v1/tiers/{id}- Get tier by IDPOST /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 membershipsPOST /api/v1/memberships/- Create membershipGET /api/v1/memberships/{id}- Get membership by IDPUT /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 paymentsPOST /api/v1/payments/- Create paymentGET /api/v1/payments/{id}- Get payment by IDPUT /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 configPOST /api/v1/payments/square/process- Process Square card paymentPOST /api/v1/payments/square/refund- Refund Square payment (admin)
Profile Questions
GET /api/v1/users/me/profile-questions- List active questions with current answersPUT /api/v1/users/me/profile-answers- Update editable answersGET /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 eventsGET /api/v1/events/upcoming- List upcoming eventsPOST /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 emailPOST /api/v1/email/test-welcome-email- Send test welcome emailPOST /api/v1/email/webhooks/smtp2go/bounce- Receive SMTP2GO bounce webhookGET /api/v1/email/bounces- List bouncesGET /api/v1/email/bounces/stats- Bounce statisticsGET /api/v1/email-templates/- List templatesPUT /api/v1/email-templates/{template_key}- Update templateGET /api/v1/feature-flags/flags- List flagsPOST /api/v1/feature-flags/flags/reload- Reload flags (super admin)
Docker Compose Commands
Basic Operations
# 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
# 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.
# 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:
- Register a new user
- Login to get access token
- Click "Authorize" button and enter:
Bearer <your_token> - Test protected endpoints
Or use curl:
# 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:
-
Personal - £5/year
- Access to member portal
- Meeting notifications
- Event participation
-
Aircraft Owners - £25/year
- All Personal benefits
- Priority event registration
- Aircraft owner resources
-
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
# 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
# Check backend connectivity and status
docker compose ps
# View backend logs for DB errors
docker compose logs backend
Clean slate restart
# 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.