diff --git a/README.md b/README.md index 1573e15..ed92bfc 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,22 @@ docker compose up -d # Access the frontend at localhost:6767 ``` +### Reverse proxy / Traefik setups + +When ExcaliDash runs behind Traefik, Nginx, or another reverse proxy, configure both containers so that API + WebSocket calls resolve correctly: + +- `FRONTEND_URL` (backend) must match the public URL that users hit (e.g. `https://excalidraw.example.com`). This controls CORS and Socket.IO origin checks. +- `BACKEND_URL` (frontend) tells the Nginx container how to reach the backend from inside Docker/Kubernetes. Override it if your reverse proxy exposes the backend under a different hostname. + +```yaml +frontend: + environment: + - BACKEND_URL=excalidash-backend.default.svc.cluster.local:8000 +backend: + environment: + - FRONTEND_URL=https://excalidraw.example.com +``` + # Development ## Clone the Repository diff --git a/docker-compose.yml b/docker-compose.yml index 5a1eaef..70ad10f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,6 +32,8 @@ services: container_name: excalidash-frontend ports: - "6767:80" + environment: + - BACKEND_URL=backend:8000 depends_on: - backend networks: diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 3468da4..9042f03 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -25,12 +25,23 @@ RUN npm run build # Production stage FROM nginx:alpine -# Copy custom nginx config +# Install envsubst (gettext) so we can template nginx config at runtime +RUN apk add --no-cache gettext + +# Copy nginx config template (will be processed at runtime) +COPY frontend/nginx.conf.template /etc/nginx/nginx.conf.template +# Also copy the original as fallback COPY frontend/nginx.conf /etc/nginx/nginx.conf +# Copy entrypoint script +COPY frontend/docker-entrypoint.sh /docker-entrypoint.sh +RUN chmod +x /docker-entrypoint.sh + # Copy built application from builder COPY --from=builder /app/frontend/dist /usr/share/nginx/html EXPOSE 80 +# Use custom entrypoint to process nginx config template +ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/docker-entrypoint.sh b/frontend/docker-entrypoint.sh new file mode 100644 index 0000000..67e5740 --- /dev/null +++ b/frontend/docker-entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +# Set default backend URL if not provided +export BACKEND_URL="${BACKEND_URL:-backend:8000}" + +echo "Configuring nginx with BACKEND_URL: ${BACKEND_URL}" + +# Substitute environment variables in nginx config template +# Only substitute BACKEND_URL, preserve nginx variables like $http_upgrade +envsubst '${BACKEND_URL}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf + +# Execute the main command (nginx) +exec "$@" + diff --git a/frontend/nginx.conf.template b/frontend/nginx.conf.template new file mode 100644 index 0000000..596d19b --- /dev/null +++ b/frontend/nginx.conf.template @@ -0,0 +1,75 @@ +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + gzip on; + gzip_vary on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + # Set maximum request body size to 50MB to handle large drawings with embedded images + client_max_body_size 50M; + + server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # API and WebSocket proxy to backend + # BACKEND_URL is substituted at container startup (default: backend:8000) + location /api/ { + proxy_pass http://${BACKEND_URL}/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_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; + + # Buffer and timeout settings for large payloads + proxy_buffering on; + proxy_buffer_size 4k; + proxy_buffers 8 4k; + proxy_busy_buffers_size 8k; + client_body_buffer_size 128k; + + # Timeouts for large uploads (300 seconds) + proxy_connect_timeout 300s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + } + + # WebSocket proxy for Socket.IO + location /socket.io/ { + proxy_pass http://${BACKEND_URL}/socket.io/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_cache_bypass $http_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; + } + + # Frontend routes + location / { + try_files $uri $uri/ /index.html; + } + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + } +} +