Container refactoring

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
James Pattinson
2026-04-26 09:43:02 +00:00
parent 0c0b5fbefe
commit 74a4e3ede8
9 changed files with 259 additions and 109 deletions
+6
View File
@@ -4,6 +4,12 @@ APP_VERSION="1.0.0"
DEBUG=True
ENVIRONMENT=development
# Gateway host port
APP_PORT=8050
APP_TLS_PORT=8443
DEV_CERT_CN=localhost
DEV_CERT_SANS=DNS:localhost,IP:127.0.0.1,IP:::1
# API Settings
API_V1_PREFIX=/api/v1
SECRET_KEY=your-secret-key-change-this-in-production
+1
View File
@@ -49,6 +49,7 @@ Thumbs.db
# Docker
docker-compose.override.yml
docker/certs/
# Uploads
uploads/
+28 -32
View File
@@ -4,23 +4,28 @@
```bash
# Start all services
docker-compose up -d
docker compose up -d
# Watch the logs until services are ready
docker-compose logs -f
docker compose logs -f
```
Wait until you see "Application startup complete", then press Ctrl+C.
**Access the API**:
- API: http://localhost:8000
- Docs: http://localhost:8000/docs
- API: http://localhost:8050/api/v1
- Docs: http://localhost:8050/docs
Set `APP_PORT` in `.env` / `.env.example` to change `8050`.
For Square payment form testing, use HTTPS at `https://localhost:8443`.
Set `APP_TLS_PORT` in `.env` / `.env.example` to change `8443`.
TLS certs are auto-generated by the gateway container on first start.
## Testing the API
### 1. Register a new user
```bash
curl -X POST "http://localhost:8000/api/v1/auth/register" \
curl -X POST "http://localhost:8050/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
@@ -32,7 +37,7 @@ curl -X POST "http://localhost:8000/api/v1/auth/register" \
### 2. Login
```bash
curl -X POST "http://localhost:8000/api/v1/auth/login-json" \
curl -X POST "http://localhost:8050/api/v1/auth/login-json" \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
@@ -44,45 +49,41 @@ Save the `access_token` from the response.
### 3. Get your profile
```bash
curl -X GET "http://localhost:8000/api/v1/users/me" \
curl -X GET "http://localhost:8050/api/v1/users/me" \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
```
### 4. List membership tiers
```bash
curl -X GET "http://localhost:8000/api/v1/tiers/"
curl -X GET "http://localhost:8050/api/v1/tiers/"
```
## Docker Compose Commands
```bash
# Start services
docker-compose up -d
docker compose up -d
# Stop services
docker-compose down
docker compose down
# View logs (all services)
docker-compose logs -f
docker compose logs -f
# View logs (specific service)
docker-compose logs -f backend
docker-compose logs -f mysql
docker compose logs -f backend
# Restart services
docker-compose restart
docker compose restart
# Rebuild after code changes
docker-compose up -d --build
docker compose up -d --build
# Check status
docker-compose ps
docker compose ps
# Access MySQL CLI (using environment variables)
docker exec -it membership_mysql mysql -u "${DATABASE_USER}" -p"${DATABASE_PASSWORD}" "${DATABASE_NAME}"
# Create database backup
docker exec membership_mysql mysqldump -u "${DATABASE_USER}" -p"${DATABASE_PASSWORD}" "${DATABASE_NAME}" > backup_$(date +%Y%m%d_%H%M%S).sql
# Tail gateway logs
docker compose logs -f gateway
```
## Default Admin Access
@@ -111,33 +112,28 @@ docker exec membership_mysql mysqldump -u "${DATABASE_USER}" -p"${DATABASE_PASSW
### Check service status
```bash
docker-compose ps
docker compose ps
```
### View all logs
```bash
docker-compose logs -f
docker compose logs -f
```
### View backend logs only
```bash
docker-compose logs -f backend
```
### View MySQL logs only
```bash
docker-compose logs -f mysql
docker compose logs -f backend
```
### Restart everything
```bash
docker-compose restart
docker compose restart
```
### Clean start (removes all data)
```bash
docker-compose down -v
docker-compose up -d
docker compose down -v
docker compose up -d
```
## Next Steps
+61 -48
View File
@@ -73,41 +73,42 @@ membership/
- SMTP2GO API key (for email notifications)
- Database password (if desired)
3. **Start the services in your preferred mode**:
3. **Start the services**:
```bash
# For development (with hot reloading)
docker-compose --profile dev up -d
# For production (optimized static files)
docker-compose --profile prod up -d
# 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
docker compose logs -f
```
Press Ctrl+C when you see "Application startup complete"
5. **Access the application**:
- Frontend: http://localhost:3500 (development) or http://localhost:8080 (production)
- API: http://localhost:8000
- API Documentation: http://localhost:8000/docs
- 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
## Frontend Development vs Production
Choose your deployment mode by using the appropriate docker-compose file:
### Development Mode (Vite)
```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
docker compose up -d
```
- Frontend served by Vite dev server on port 3500
- 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:3500
- Access at: http://localhost:8050
### Production Mode (Nginx)
```bash
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
docker compose --profile prod up -d frontend-prod backend
```
- Frontend served by Nginx on port 8050
- Optimized static files, production-ready
@@ -120,15 +121,30 @@ 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
```bash
# Stop all services
docker compose -f docker-compose.yml -f docker-compose.dev.yml down
docker compose -f docker-compose.yml -f docker-compose.prod.yml down
docker compose down
# Stop production profile services
docker compose --profile prod down
```
## Default Credentials
@@ -182,29 +198,29 @@ docker compose -f docker-compose.yml -f docker-compose.prod.yml down
```bash
# Start all services
docker-compose up -d
docker compose up -d
# View logs (all services)
docker-compose logs -f
docker compose logs -f
# View logs (specific service)
docker-compose logs -f backend
docker-compose logs -f mysql
docker compose logs -f backend
docker compose logs -f gateway
# Stop services
docker-compose down
docker compose down
# Stop and remove volumes (clean slate)
docker-compose down -v
docker compose down -v
# Restart services
docker-compose restart
docker compose restart
# Rebuild after code changes
docker-compose up -d --build
docker compose up -d --build
# Check service status
docker-compose ps
docker compose ps
```
### Database Operations
@@ -242,7 +258,7 @@ sudo docker compose exec backend alembic history
## API Testing
You can use the interactive API documentation at http://localhost:8000/docs to test endpoints:
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
@@ -253,7 +269,7 @@ Or use curl:
```bash
# Register
curl -X POST "http://localhost:8000/api/v1/auth/register" \
curl -X POST "http://localhost:8050/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
@@ -263,7 +279,7 @@ curl -X POST "http://localhost:8000/api/v1/auth/register" \
}'
# Login
curl -X POST "http://localhost:8000/api/v1/auth/login-json" \
curl -X POST "http://localhost:8050/api/v1/auth/login-json" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
@@ -271,7 +287,7 @@ curl -X POST "http://localhost:8000/api/v1/auth/login-json" \
}'
# Get profile (replace TOKEN with actual token)
curl -X GET "http://localhost:8000/api/v1/users/me" \
curl -X GET "http://localhost:8050/api/v1/users/me" \
-H "Authorization: Bearer TOKEN"
```
@@ -308,45 +324,42 @@ The system comes with three pre-configured tiers:
### Services not starting
```bash
# Check status of all services
docker-compose ps
docker compose ps
# View all logs
docker-compose logs
docker compose logs
# View specific service logs
docker-compose logs mysql
docker-compose logs backend
docker compose logs gateway
docker compose logs backend
# Restart all services
docker-compose restart
docker compose restart
# Full restart with rebuild
docker-compose down
docker-compose up -d --build
docker compose down
docker compose up -d --build
```
### Database connection issues
```bash
# Check if MySQL is healthy
docker-compose ps
# Check backend connectivity and status
docker compose ps
# View MySQL logs for errors
docker-compose logs mysql
# Wait for MySQL to be fully ready (may take 30 seconds on first start)
docker-compose logs -f mysql
# View backend logs for DB errors
docker compose logs backend
```
### Clean slate restart
```bash
# Stop everything and remove volumes
docker-compose down -v
docker compose down -v
# Start fresh
docker-compose up -d
docker compose up -d
# Wait for initialization
docker-compose logs -f
docker compose logs -f
```
## Next Steps
+53 -28
View File
@@ -42,8 +42,8 @@ services:
- ACCESS_TOKEN_EXPIRE_MINUTES=${ACCESS_TOKEN_EXPIRE_MINUTES}
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "6000:8000" # Only expose backend API to host
expose:
- "8000"
volumes:
- ./backend/app:/app/app
- ./backend/alembic:/app/alembic
@@ -55,35 +55,60 @@ services:
# mysql:
# condition: service_healthy
# frontend:
# build:
# context: ./frontend
# dockerfile: Dockerfile
# target: development
# restart: unless-stopped
# environment:
# - VITE_HOST_CHECK=false
# - VITE_ALLOWED_HOSTS=${VITE_ALLOWED_HOSTS}
# ports:
# - "8050:3000" # Expose frontend to host
# volumes:
# - ./frontend/src:/app/src
# - ./frontend/public:/app/public
# - ./frontend/vite.config.ts:/app/vite.config.ts
# depends_on:
# - backend
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
target: development
restart: unless-stopped
env_file:
- .env
environment:
- VITE_ALLOWED_HOSTS=${VITE_ALLOWED_HOSTS}
expose:
- "3000"
volumes:
- ./frontend/src:/app/src
- ./frontend/public:/app/public
- ./frontend/vite.config.ts:/app/vite.config.ts
- ./frontend/index.html:/app/index.html
depends_on:
- backend
gateway:
build:
context: ./docker/gateway
dockerfile: Dockerfile
restart: unless-stopped
env_file:
- .env
environment:
- DEV_CERT_CN=${DEV_CERT_CN:-localhost}
- DEV_CERT_SANS=${DEV_CERT_SANS:-DNS:localhost,IP:127.0.0.1,IP:::1}
ports:
- "${APP_PORT:-8050}:80"
- "${APP_TLS_PORT:-8443}:443"
volumes:
- ./docker/gateway/nginx.dev.conf:/etc/nginx/conf.d/default.conf:ro
- gateway_certs:/etc/nginx/certs
depends_on:
- backend
- frontend
frontend-prod:
build:
context: ./frontend
dockerfile: Dockerfile
target: production
restart: unless-stopped
ports:
- "8050:80" # Nginx default port
depends_on:
- backend
build:
context: ./frontend
dockerfile: Dockerfile
target: production
restart: unless-stopped
ports:
- "${APP_PORT:-8050}:80" # Nginx default port
depends_on:
- backend
profiles:
- prod
volumes:
# mysql_data:
uploads_data:
gateway_certs:
+8
View File
@@ -0,0 +1,8 @@
FROM nginx:alpine
RUN apk add --no-cache openssl
COPY docker-entrypoint-dev.sh /usr/local/bin/docker-entrypoint-dev.sh
RUN chmod +x /usr/local/bin/docker-entrypoint-dev.sh
CMD ["/usr/local/bin/docker-entrypoint-dev.sh"]
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env sh
set -eu
CERT_DIR="/etc/nginx/certs"
CERT_FILE="$CERT_DIR/dev.crt"
KEY_FILE="$CERT_DIR/dev.key"
CERT_CN="${DEV_CERT_CN:-localhost}"
CERT_SANS="${DEV_CERT_SANS:-DNS:localhost,IP:127.0.0.1,IP:::1}"
mkdir -p "$CERT_DIR"
if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
echo "Generating self-signed TLS certificate for CN=$CERT_CN"
openssl req -x509 -nodes -newkey rsa:2048 \
-keyout "$KEY_FILE" \
-out "$CERT_FILE" \
-days 365 \
-subj "/C=GB/ST=Dev/L=Dev/O=SASA/OU=Membership/CN=$CERT_CN" \
-addext "subjectAltName=$CERT_SANS"
fi
exec nginx -g 'daemon off;'
+79
View File
@@ -0,0 +1,79 @@
server {
listen 80;
server_name _;
# Keep HTTP available in dev, but use HTTPS for Square Web Payments.
location /api/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~ ^/(docs|redoc|openapi.json)$ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
proxy_pass http://frontend:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 443 ssl;
server_name _;
ssl_certificate /etc/nginx/certs/dev.crt;
ssl_certificate_key /etc/nginx/certs/dev.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_prefer_server_ciphers off;
# API routes to backend service
location /api/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# FastAPI docs and schema for local development
location ~ ^/(docs|redoc|openapi.json)$ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# All other requests route to Vite dev server
location / {
proxy_pass http://frontend:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ export default defineConfig({
host: true,
port: 3000,
strictPort: true,
allowedHosts: process.env.VITE_ALLOWED_HOSTS ? process.env.VITE_ALLOWED_HOSTS.split(',') : ['sasaprod', 'localhost'],
allowedHosts: process.env.VITE_ALLOWED_HOSTS ? process.env.VITE_ALLOWED_HOSTS.split(',') : true,
watch: {
usePolling: true
},