diff --git a/.env.prod b/.env.prod new file mode 100644 index 0000000..aaaaec8 --- /dev/null +++ b/.env.prod @@ -0,0 +1,8 @@ +# Committed prod defaults (see .env for the full explanation of the cascade). + +# The relay itself still binds 0.0.0.0:8091 inside its own container (see +# PUZZLE_RELAY_LISTEN in .env) - it's never published to the host or the +# internet directly. Browsers instead reach it through this same domain, via +# the /puzzle-ws/ proxy in docker/nginx/default.conf, which forwards to the +# puzzle-relay container over the docker network. +PUZZLE_RELAY_PUBLIC_URL=wss://dmtools.fvandenberg.nl/puzzle-ws/ diff --git a/docker-compose.yml b/docker-compose.yml index 6529738..01bb144 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,6 +33,21 @@ services: condition: service_healthy restart: unless-stopped + puzzle-relay: + build: + context: . + dockerfile: docker/php/Dockerfile + container_name: dmtools-puzzle-relay + command: ["php", "bin/console", "app:puzzle-relay"] + env_file: + - path: .env + - path: .env.local + required: false + volumes: + - .:/var/www/html:cached + - php_var:/var/www/html/var + restart: unless-stopped + nginx: image: nginx:1.30-alpine container_name: dmtools-nginx @@ -43,6 +58,7 @@ services: - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - php + - puzzle-relay restart: unless-stopped database: diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf index 22649c6..76c75df 100644 --- a/docker/nginx/default.conf +++ b/docker/nginx/default.conf @@ -48,6 +48,24 @@ server { return 404; } + # Proxies the puzzle relay's WebSocket connections (src/Command/ + # PuzzleRelayCommand.php, a separate long-running container - see + # docker-compose.yml) through this same domain, so the browser can use a + # plain wss://dmtools.fvandenberg.nl/puzzle-ws/ URL instead of a second + # exposed port. NPM must also have "Websockets Support" enabled for this + # proxy host, or the Upgrade header never reaches here. + location /puzzle-ws/ { + resolver 127.0.0.11 valid=10s; + set $puzzle_relay dmtools-puzzle-relay:8091; + proxy_pass http://$puzzle_relay/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_read_timeout 1h; + proxy_send_timeout 1h; + } + error_log /var/log/nginx/dmtools_error.log; access_log /var/log/nginx/dmtools_access.log; diff --git a/docker/restart.sh b/docker/restart.sh index be7a7d5..2e04481 100755 --- a/docker/restart.sh +++ b/docker/restart.sh @@ -23,7 +23,7 @@ done echo "Restarting stack: $PROJECT" (cd "$ROOT_DIR" && docker compose -p "$PROJECT" -f docker-compose.yml down --remove-orphans) || true -docker rm -f dmtools-php dmtools-nginx dmtools-db 2>/dev/null || true +docker rm -f dmtools-php dmtools-puzzle-relay dmtools-nginx dmtools-db 2>/dev/null || true for net in $(docker network ls -q --filter "name=^${PROJECT}_" 2>/dev/null); do docker network rm "$net" || true done diff --git a/docker/setup.sh b/docker/setup.sh index 3a56290..2a26ea3 100755 --- a/docker/setup.sh +++ b/docker/setup.sh @@ -65,7 +65,7 @@ fi # docker-compose v1 can choke recreating a container in place on any config # change; removing them first sidesteps that. Safe: state lives in named volumes. -dc rm -fs php nginx database 2>/dev/null || true +dc rm -fs php puzzle-relay nginx database 2>/dev/null || true BUILD_ARGS=() [ "$REBUILD" -eq 1 ] && BUILD_ARGS+=("--build") @@ -97,7 +97,7 @@ if grep -q '^APP_SECRET=$' "$ROOT_DIR/.env" 2>/dev/null \ && ! grep -q '^APP_SECRET=' "$ROOT_DIR/.env.local" 2>/dev/null; then echo "Generating APP_SECRET in .env.local..." printf 'APP_SECRET=%s\n' "$(openssl rand -hex 16)" >> "$ROOT_DIR/.env.local" - dc up -d php # pick up the new env value + dc up -d php puzzle-relay # pick up the new env value fi echo "Creating database if it doesn't exist..." @@ -116,8 +116,10 @@ pexec chown -R www-data:www-data var # php.ini sets opcache.validate_timestamps=0, so php-fpm keeps serving the # old compiled code/templates until it restarts. Bounce it so a --no-build -# run (git pull + this script) actually picks up the new cache. -dc restart php +# run (git pull + this script) actually picks up the new cache. puzzle-relay +# is a single long-running process, not php-fpm workers, but it's exactly as +# stale otherwise: it keeps running whatever code was loaded when it started. +dc restart php puzzle-relay # Make sure Nginx Proxy Manager can reach this stack's nginx by name. # Harmless (and a no-op) if already connected; NPM keeps it across restarts.