#!/usr/bin/env bash # Run the whole browser stack on this machine, without nginx. # # ./scripts/dev-web.sh # ./scripts/dev-web.sh --reset # wipe the local dev database first # # It builds the server, the gateway and the browser app, creates a local TLS # identity under target/dev-web/, and starts: # # game server 127.0.0.1:11525 (target/dev-web/server.db) # webd 127.0.0.1:8890 # static app 127.0.0.1:8899 (web/play/?ws=ws://127.0.0.1:8890/ws) # # The page and the WebSocket are on different loopback ports. The app only # honours ?ws= on localhost, so this cannot be used to redirect a real player's # password. In production nginx keeps both on one origin. # # Environment overrides: # ZAGAN_DEV_GAME_PORT=11525 # ZAGAN_DEV_GATEWAY_PORT=8890 # ZAGAN_DEV_WEB_PORT=8899 # ZAGAN_DEV_DIR=target/dev-web # ZAGAN_DEV_OPEN=1 open the URL in the desktop browser set -euo pipefail ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) cd "$ROOT" DEV_DIR=${ZAGAN_DEV_DIR:-"$ROOT/target/dev-web"} mkdir -p "$DEV_DIR" DEV_DIR=$(cd "$DEV_DIR" && pwd) GAME_PORT=${ZAGAN_DEV_GAME_PORT:-11525} GATEWAY_PORT=${ZAGAN_DEV_GATEWAY_PORT:-8890} WEB_PORT=${ZAGAN_DEV_WEB_PORT:-8899} ORIGIN="http://127.0.0.1:$WEB_PORT" URL="$ORIGIN/play/?ws=ws%3A%2F%2F127.0.0.1%3A$GATEWAY_PORT%2Fws" die() { echo "dev-web: $*" >&2; exit 1; } command -v cargo >/dev/null || die "cargo is required" command -v openssl >/dev/null || die "openssl is required" command -v python3 >/dev/null || die "python3 is required for the static server" command -v ss >/dev/null || die "ss is required to wait for the dev ports" if [ "${1:-}" = "--reset" ]; then rm -f "$DEV_DIR/server.db" echo "==> removed $DEV_DIR/server.db" fi echo "==> building server, gateway and browser app" cargo build --quiet --bin server cargo build --quiet -p zagan-bet-webd --bin zagan-bet-webd if command -v npm >/dev/null 2>&1; then if [ ! -d web/play/node_modules ]; then (cd web/play && npm ci --silent) fi (cd web/play && npm run build --silent >/dev/null) fi if [ ! -f "$DEV_DIR/identity.p12" ] || [ ! -f "$DEV_DIR/cert.pem" ]; then echo "==> creating a local TLS identity" (cd "$DEV_DIR" && "$ROOT/target/debug/server" --init >/dev/null) fi fingerprint=$(openssl x509 -in "$DEV_DIR/cert.pem" -pubkey -noout \ | openssl pkey -pubin -outform DER \ | sha256sum | awk '{print $1}') [ -n "$fingerprint" ] || die "could not read the local certificate fingerprint" for port in "$GAME_PORT" "$GATEWAY_PORT" "$WEB_PORT"; do if (exec 3<>"/dev/tcp/127.0.0.1/$port") 2>/dev/null; then exec 3>&- die "port $port is already in use; stop the other process or set the ZAGAN_DEV_*_PORT override" fi done pids=() cleanup() { trap - EXIT INT TERM for pid in "${pids[@]}"; do kill "$pid" 2>/dev/null || true done wait 2>/dev/null || true } trap cleanup EXIT INT TERM echo "==> starting game server on 127.0.0.1:$GAME_PORT" # The server loads identity.p12 from its working directory. Run it inside the # dev directory so it never touches the repository's own identity files. (cd "$DEV_DIR" && exec env SERVER_DB="$DEV_DIR/server.db" \ "$ROOT/target/debug/server" -i 127.0.0.1 -p "$GAME_PORT") \ >"$DEV_DIR/server.log" 2>&1 & pids+=($!) echo "==> starting gateway on 127.0.0.1:$GATEWAY_PORT" ZAGAN_WEB_BIND="127.0.0.1:$GATEWAY_PORT" \ ZAGAN_WEB_ORIGIN="$ORIGIN" \ ZAGAN_UPSTREAM="127.0.0.1:$GAME_PORT" \ ZAGAN_UPSTREAM_FINGERPRINT="$fingerprint" \ ZAGAN_WEB_COOKIE_SECURE=0 \ "$ROOT/target/debug/zagan-bet-webd" >"$DEV_DIR/webd.log" 2>&1 & pids+=($!) echo "==> starting static app on 127.0.0.1:$WEB_PORT" python3 -m http.server "$WEB_PORT" --bind 127.0.0.1 --directory "$ROOT/web" \ >"$DEV_DIR/static.log" 2>&1 & pids+=($!) wait_port() { local port=$1 name=$2 for _ in $(seq 1 100); do if ss -ltn 2>/dev/null \ | awk -v p=":$port" '$4 ~ p { found = 1 } END { exit found ? 0 : 1 }'; then return 0 fi sleep 0.1 done echo "dev-web: $name did not listen on $port; see $DEV_DIR" >&2 return 1 } wait_port "$GAME_PORT" "the game server" || exit 1 wait_port "$GATEWAY_PORT" "the gateway" || exit 1 wait_port "$WEB_PORT" "the static server" || exit 1 cat </dev/null 2>&1 && xdg-open "$URL" >/dev/null 2>&1 || true fi # Stop the script when any child dies unexpectedly as well. wait -n || true