productionize
This commit is contained in:
@@ -33,6 +33,8 @@ Thumbs.db
|
|||||||
# Environment
|
# Environment
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
|
.env.production
|
||||||
|
.env.production.example
|
||||||
|
|
||||||
# Database (will be persisted via volumes)
|
# Database (will be persisted via volumes)
|
||||||
*.db
|
*.db
|
||||||
@@ -51,6 +53,9 @@ docker-compose*.yml
|
|||||||
README.md
|
README.md
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
|
|
||||||
|
# Nginx config
|
||||||
|
nginx.conf.example
|
||||||
|
|
||||||
# Frontend build output (not needed during image build)
|
# Frontend build output (not needed during image build)
|
||||||
frontend/frontend/dist
|
frontend/frontend/dist
|
||||||
backend/static/index.html
|
backend/static/index.html
|
||||||
|
|||||||
23
.env.production.example
Normal file
23
.env.production.example
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Production Environment Variables
|
||||||
|
# Copy this file to .env.production and fill in the values
|
||||||
|
|
||||||
|
# Backend port (defaults to 5001 if not set)
|
||||||
|
PORT=5001
|
||||||
|
|
||||||
|
# CORS origins (use your domain in production, or * to allow all)
|
||||||
|
CORS_ORIGINS=https://trivia.torrtle.co
|
||||||
|
|
||||||
|
# Authelia OIDC Configuration
|
||||||
|
# REQUIRED: Set these values to match your Authelia instance
|
||||||
|
OIDC_ISSUER=https://auth.torrtle.co
|
||||||
|
OIDC_CLIENT_ID=trivia-app
|
||||||
|
OIDC_CLIENT_SECRET=your_client_secret_here
|
||||||
|
|
||||||
|
# OAuth redirect URI (must match your domain)
|
||||||
|
OIDC_REDIRECT_URI=https://trivia.torrtle.co/api/auth/callback
|
||||||
|
|
||||||
|
# Frontend URL (must match your domain)
|
||||||
|
FRONTEND_URL=https://trivia.torrtle.co
|
||||||
|
|
||||||
|
# Cookie security (set to 'true' in production with HTTPS)
|
||||||
|
SESSION_COOKIE_SECURE=true
|
||||||
42
CLAUDE.md
42
CLAUDE.md
@@ -111,6 +111,48 @@ FRONTEND_PORT=4000 docker compose up
|
|||||||
- Named volume for `node_modules` to avoid host/container conflicts
|
- Named volume for `node_modules` to avoid host/container conflicts
|
||||||
- Health check on backend before starting frontend
|
- Health check on backend before starting frontend
|
||||||
|
|
||||||
|
### Docker Production (Single Container)
|
||||||
|
|
||||||
|
For production deployment with a single URL serving both frontend and backend:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build production image (multi-stage: builds React, copies to Flask static/)
|
||||||
|
docker build -t trivia-app:latest .
|
||||||
|
|
||||||
|
# Run with production compose file
|
||||||
|
docker compose -f docker-compose.production.yml up -d
|
||||||
|
|
||||||
|
# Or run standalone container
|
||||||
|
docker run -d \
|
||||||
|
-p 5001:5001 \
|
||||||
|
-v trivia-db:/app/backend/instance \
|
||||||
|
-v trivia-images:/app/backend/static/images \
|
||||||
|
-v trivia-audio:/app/backend/static/audio \
|
||||||
|
--env-file .env.production \
|
||||||
|
trivia-app:latest
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose -f docker-compose.production.yml logs -f
|
||||||
|
|
||||||
|
# Run migrations
|
||||||
|
docker compose -f docker-compose.production.yml exec backend uv run flask db upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
**Production Architecture:**
|
||||||
|
- Multi-stage `Dockerfile`: Stage 1 builds React frontend, Stage 2 runs Flask with built frontend
|
||||||
|
- Single Flask server serves both React SPA and API endpoints
|
||||||
|
- All requests go to same origin (e.g., `https://trivia.torrtle.co`)
|
||||||
|
- React uses relative URLs (`/api/*`, `/socket.io`) - no proxy needed
|
||||||
|
- Flask routing: API requests go to blueprints, all other routes serve `index.html` for React Router
|
||||||
|
- Typically deployed behind nginx/Caddy reverse proxy for HTTPS/SSL
|
||||||
|
- Volumes persist database, images, and audio files
|
||||||
|
|
||||||
|
**Environment:**
|
||||||
|
- Copy `.env.production.example` to `.env.production` and configure
|
||||||
|
- Set `CORS_ORIGINS` to your domain (e.g., `https://trivia.torrtle.co`)
|
||||||
|
- Set `SESSION_COOKIE_SECURE=true` for HTTPS
|
||||||
|
- Configure OIDC URLs to match your domain
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Application Factory Pattern
|
### Application Factory Pattern
|
||||||
|
|||||||
57
Dockerfile
Normal file
57
Dockerfile
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Multi-stage build for production
|
||||||
|
# Stage 1: Build React frontend
|
||||||
|
FROM node:20-slim AS frontend-builder
|
||||||
|
|
||||||
|
WORKDIR /app/frontend
|
||||||
|
|
||||||
|
# Copy package files from nested directory
|
||||||
|
COPY frontend/frontend/package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm ci --only=production
|
||||||
|
|
||||||
|
# Copy frontend source
|
||||||
|
COPY frontend/frontend ./
|
||||||
|
|
||||||
|
# Build for production (outputs to ../backend/static)
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Stage 2: Production Flask app
|
||||||
|
FROM python:3.14-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install uv for dependency management
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||||
|
|
||||||
|
# Copy Python dependency files
|
||||||
|
COPY pyproject.toml uv.lock ./
|
||||||
|
|
||||||
|
# Install Python dependencies
|
||||||
|
RUN uv sync --frozen --no-dev
|
||||||
|
|
||||||
|
# Copy backend code
|
||||||
|
COPY backend ./backend
|
||||||
|
COPY main.py ./
|
||||||
|
COPY migrations ./migrations
|
||||||
|
|
||||||
|
# Copy built frontend from frontend-builder stage
|
||||||
|
COPY --from=frontend-builder /app/backend/static ./backend/static
|
||||||
|
|
||||||
|
# Create directories for uploads and database
|
||||||
|
RUN mkdir -p backend/instance backend/static/images backend/static/audio
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV FLASK_ENV=production
|
||||||
|
ENV PORT=5001
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 5001
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5001/api/health')"
|
||||||
|
|
||||||
|
# Run Flask app with socketio support
|
||||||
|
CMD ["uv", "run", "python", "main.py"]
|
||||||
232
README.production.md
Normal file
232
README.production.md
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
# Production Deployment Guide
|
||||||
|
|
||||||
|
This guide covers deploying the trivia application to production with a single URL serving both the frontend and backend.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
In production:
|
||||||
|
- Single Flask server serves both the React SPA and API endpoints
|
||||||
|
- Multi-stage Docker build: React is built and copied into Flask's static folder
|
||||||
|
- All requests go to the same domain (e.g., `https://trivia.torrtle.co`)
|
||||||
|
- Nginx/Caddy reverse proxy handles HTTPS and forwards to Flask
|
||||||
|
- WebSocket connections work seamlessly (same origin)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Configure Environment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy and edit production environment file
|
||||||
|
cp .env.production.example .env.production
|
||||||
|
|
||||||
|
# Edit .env.production with your values:
|
||||||
|
# - Set CORS_ORIGINS to your domain
|
||||||
|
# - Configure OIDC/Authelia settings
|
||||||
|
# - Set SESSION_COOKIE_SECURE=true
|
||||||
|
nano .env.production
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Build and Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the production image
|
||||||
|
docker build -t trivia-app:latest .
|
||||||
|
|
||||||
|
# Start all services
|
||||||
|
docker compose -f docker-compose.production.yml up -d
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose -f docker-compose.production.yml logs -f backend
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Run Database Migrations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Initialize database (first time only)
|
||||||
|
docker compose -f docker-compose.production.yml exec backend uv run flask db upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Configure Reverse Proxy
|
||||||
|
|
||||||
|
#### Option A: Nginx
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Copy example config
|
||||||
|
sudo cp nginx.conf.example /etc/nginx/sites-available/trivia
|
||||||
|
|
||||||
|
# Update domain and SSL certificate paths
|
||||||
|
sudo nano /etc/nginx/sites-available/trivia
|
||||||
|
|
||||||
|
# Enable site
|
||||||
|
sudo ln -s /etc/nginx/sites-available/trivia /etc/nginx/sites-enabled/
|
||||||
|
|
||||||
|
# Test configuration
|
||||||
|
sudo nginx -t
|
||||||
|
|
||||||
|
# Reload nginx
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option B: Caddy (Simpler)
|
||||||
|
|
||||||
|
Create `/etc/caddy/Caddyfile`:
|
||||||
|
|
||||||
|
```caddy
|
||||||
|
trivia.torrtle.co {
|
||||||
|
reverse_proxy localhost:5001
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Caddy automatically handles HTTPS with Let's Encrypt!
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
Once deployed, your application will be available at:
|
||||||
|
|
||||||
|
- **Main App**: `https://trivia.torrtle.co`
|
||||||
|
- **API**: `https://trivia.torrtle.co/api/*`
|
||||||
|
- **WebSocket**: `wss://trivia.torrtle.co/socket.io`
|
||||||
|
- **Flower (optional)**: `https://trivia.torrtle.co/flower/` (if configured in nginx)
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# All services
|
||||||
|
docker compose -f docker-compose.production.yml logs -f
|
||||||
|
|
||||||
|
# Specific service
|
||||||
|
docker compose -f docker-compose.production.yml logs -f backend
|
||||||
|
docker compose -f docker-compose.production.yml logs -f celery-worker
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pull latest code
|
||||||
|
git pull
|
||||||
|
|
||||||
|
# Rebuild and restart
|
||||||
|
docker compose -f docker-compose.production.yml up -d --build
|
||||||
|
|
||||||
|
# Run any new migrations
|
||||||
|
docker compose -f docker-compose.production.yml exec backend uv run flask db upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backup Database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backup database volume
|
||||||
|
docker run --rm \
|
||||||
|
-v trivia-app_trivia-db:/data \
|
||||||
|
-v $(pwd)/backups:/backup \
|
||||||
|
alpine tar czf /backup/trivia-db-$(date +%Y%m%d).tar.gz -C /data .
|
||||||
|
|
||||||
|
# Backup images
|
||||||
|
docker run --rm \
|
||||||
|
-v trivia-app_trivia-images:/data \
|
||||||
|
-v $(pwd)/backups:/backup \
|
||||||
|
alpine tar czf /backup/trivia-images-$(date +%Y%m%d).tar.gz -C /data .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore Database
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop services
|
||||||
|
docker compose -f docker-compose.production.yml down
|
||||||
|
|
||||||
|
# Restore from backup
|
||||||
|
docker run --rm \
|
||||||
|
-v trivia-app_trivia-db:/data \
|
||||||
|
-v $(pwd)/backups:/backup \
|
||||||
|
alpine sh -c "cd /data && tar xzf /backup/trivia-db-YYYYMMDD.tar.gz"
|
||||||
|
|
||||||
|
# Start services
|
||||||
|
docker compose -f docker-compose.production.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scaling
|
||||||
|
|
||||||
|
### Run Multiple Workers
|
||||||
|
|
||||||
|
Edit `docker-compose.production.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
celery-worker:
|
||||||
|
# ... existing config ...
|
||||||
|
deploy:
|
||||||
|
replicas: 3 # Run 3 worker instances
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use External Database
|
||||||
|
|
||||||
|
For production at scale, consider using PostgreSQL instead of SQLite:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.production.yml
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: trivia
|
||||||
|
POSTGRES_USER: trivia
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
backend:
|
||||||
|
environment:
|
||||||
|
- DATABASE_URI=postgresql://trivia:${DB_PASSWORD}@postgres:5432/trivia
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
### Health Checks
|
||||||
|
|
||||||
|
The application includes a health check endpoint:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://trivia.torrtle.co/api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
### Celery Flower
|
||||||
|
|
||||||
|
Access Celery task monitoring at `http://localhost:5555` or configure nginx to expose it at `/flower/`.
|
||||||
|
|
||||||
|
## Security Checklist
|
||||||
|
|
||||||
|
- [ ] Set `SESSION_COOKIE_SECURE=true` in `.env.production`
|
||||||
|
- [ ] Set `CORS_ORIGINS` to your specific domain (not `*`)
|
||||||
|
- [ ] Use strong `OIDC_CLIENT_SECRET`
|
||||||
|
- [ ] Enable HTTPS with valid SSL certificate
|
||||||
|
- [ ] Keep Docker images up to date
|
||||||
|
- [ ] Regular database backups
|
||||||
|
- [ ] Restrict Flower access (don't expose publicly)
|
||||||
|
- [ ] Use firewall to restrict port 5001 to localhost only
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### WebSocket Connection Issues
|
||||||
|
|
||||||
|
Ensure your reverse proxy is configured for WebSocket upgrades:
|
||||||
|
- Nginx: `proxy_set_header Upgrade $http_upgrade;`
|
||||||
|
- Caddy: Handles automatically
|
||||||
|
|
||||||
|
### CORS Errors
|
||||||
|
|
||||||
|
Check `CORS_ORIGINS` in `.env.production` matches your domain exactly (including https://).
|
||||||
|
|
||||||
|
### 404 on Frontend Routes
|
||||||
|
|
||||||
|
Flask's catch-all route should serve `index.html` for all non-API routes. Check that React build files exist in `backend/static/`.
|
||||||
|
|
||||||
|
### Database Migration Errors
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check current migration version
|
||||||
|
docker compose -f docker-compose.production.yml exec backend uv run flask db current
|
||||||
|
|
||||||
|
# Force to specific version (if needed)
|
||||||
|
docker compose -f docker-compose.production.yml exec backend uv run flask db stamp head
|
||||||
|
```
|
||||||
100
docker-compose.production.yml
Normal file
100
docker-compose.production.yml
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
services:
|
||||||
|
redis:
|
||||||
|
image: redis:7-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- trivia-network
|
||||||
|
volumes:
|
||||||
|
- redis-data:/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "${PORT:-5001}:5001"
|
||||||
|
environment:
|
||||||
|
- FLASK_ENV=production
|
||||||
|
- PORT=5001
|
||||||
|
- DATABASE_URI=sqlite:////app/backend/instance/trivia.db
|
||||||
|
- CORS_ORIGINS=${CORS_ORIGINS:-*}
|
||||||
|
- CELERY_BROKER_URL=redis://redis:6379/0
|
||||||
|
- CELERY_RESULT_BACKEND=redis://redis:6379/0
|
||||||
|
# OIDC/Authelia configuration
|
||||||
|
- OIDC_ISSUER=${OIDC_ISSUER}
|
||||||
|
- OIDC_CLIENT_ID=${OIDC_CLIENT_ID:-trivia-app}
|
||||||
|
- OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
|
||||||
|
- OIDC_REDIRECT_URI=${OIDC_REDIRECT_URI}
|
||||||
|
- FRONTEND_URL=${FRONTEND_URL}
|
||||||
|
- SESSION_COOKIE_SECURE=${SESSION_COOKIE_SECURE:-true}
|
||||||
|
volumes:
|
||||||
|
# Persist database
|
||||||
|
- trivia-db:/app/backend/instance
|
||||||
|
# Persist uploaded images
|
||||||
|
- trivia-images:/app/backend/static/images
|
||||||
|
# Persist audio files
|
||||||
|
- trivia-audio:/app/backend/static/audio
|
||||||
|
depends_on:
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- trivia-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5001/api/health')"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
|
||||||
|
celery-worker:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
command: uv run celery -A backend.celery_app worker --loglevel=info
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- FLASK_ENV=production
|
||||||
|
- CELERY_BROKER_URL=redis://redis:6379/0
|
||||||
|
- CELERY_RESULT_BACKEND=redis://redis:6379/0
|
||||||
|
- DATABASE_URI=sqlite:////app/backend/instance/trivia.db
|
||||||
|
volumes:
|
||||||
|
- trivia-db:/app/backend/instance
|
||||||
|
- trivia-audio:/app/backend/static/audio
|
||||||
|
depends_on:
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- trivia-network
|
||||||
|
|
||||||
|
celery-flower:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
command: uv run celery -A backend.celery_app flower --port=5555
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5555:5555"
|
||||||
|
environment:
|
||||||
|
- CELERY_BROKER_URL=redis://redis:6379/0
|
||||||
|
- CELERY_RESULT_BACKEND=redis://redis:6379/0
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
- celery-worker
|
||||||
|
networks:
|
||||||
|
- trivia-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
trivia-network:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis-data:
|
||||||
|
trivia-db:
|
||||||
|
trivia-images:
|
||||||
|
trivia-audio:
|
||||||
59
nginx.conf.example
Normal file
59
nginx.conf.example
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# Example nginx configuration for production deployment
|
||||||
|
# This reverse proxies to the Flask app running in Docker
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name trivia.torrtle.co;
|
||||||
|
|
||||||
|
# Redirect HTTP to HTTPS
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name trivia.torrtle.co;
|
||||||
|
|
||||||
|
# SSL certificate configuration (adjust paths as needed)
|
||||||
|
ssl_certificate /etc/letsencrypt/live/trivia.torrtle.co/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/trivia.torrtle.co/privkey.pem;
|
||||||
|
|
||||||
|
# SSL security settings
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
# Client upload size (for image uploads)
|
||||||
|
client_max_body_size 10M;
|
||||||
|
|
||||||
|
# Proxy to Flask app
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:5001;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
# Standard proxy headers
|
||||||
|
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;
|
||||||
|
proxy_set_header X-Forwarded-Host $server_name;
|
||||||
|
|
||||||
|
# WebSocket support for Socket.IO
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
# Timeouts for WebSocket connections
|
||||||
|
proxy_connect_timeout 7d;
|
||||||
|
proxy_send_timeout 7d;
|
||||||
|
proxy_read_timeout 7d;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optional: Separate location block for Celery Flower monitoring
|
||||||
|
location /flower/ {
|
||||||
|
proxy_pass http://localhost:5555/;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user