diff --git a/scripts/e2e-camera.sh b/scripts/e2e-camera.sh index 25932a1..19433e9 100755 --- a/scripts/e2e-camera.sh +++ b/scripts/e2e-camera.sh @@ -54,6 +54,18 @@ # password each run. Port 3042 must be free. # AUTH=supabase use that real user's token for the API phases too # (default is the local dev-auth issuer) +# +# SERVE=1 not a test: bring the whole stack up for working on +# the dashboard and hold it until stopped (SIGTERM or +# Ctrl-C tears it down and deletes the seeded farm). +# Seeds the user as `owner` with every printer in +# printers.toml, runs the web console with hot +# reload, and writes the sign-in to a 0600 file whose +# path it prints (the password is never printed). +# Implies BROWSER=1. Skips every check. +# APP_HOST=127.0.0.1 the interface the web console binds. Loopback by +# default because it serves a password sign-in; set +# 0.0.0.0 only on a network you trust. set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" @@ -76,9 +88,12 @@ R2_ENDPOINT="${R2_ENDPOINT:-http://localhost:9000}" export DATABASE_URL VALKEY_URL EDGE_JWT_SECRET PORT export R2_ACCESS_KEY_ID R2_SECRET_ACCESS_KEY R2_ENDPOINT +SERVE="${SERVE:-0}" BROWSER="${BROWSER:-0}" +[ "$SERVE" = "1" ] && BROWSER=1 AUTH="${AUTH:-dev}" [ "$BROWSER" = "1" ] && AUTH=supabase +APP_HOST="${APP_HOST:-127.0.0.1}" APP_DIR="${APP_DIR:-$REPO_ROOT/../continuum-app}" APP_PORT="${APP_PORT:-3042}" CHROME_BIN="${CHROME_BIN:-/usr/bin/google-chrome}" @@ -123,6 +138,7 @@ cleanup() { echo "--- proxy log (tail) ---"; tail -30 "$WORK/proxy.log" 2>/dev/null || true fi rm -rf "$WORK" + [ -n "${LOGIN_FILE:-}" ] && rm -f "$LOGIN_FILE" # A go2rtc that outlived us is a leak, not a detail. sleep 0.5 if ps -eo comm | grep -qx go2rtc; then @@ -192,13 +208,28 @@ EDGE_PROXY_TOKEN="e2e-$(date +%s)" ORG_ID="$(psql_q "INSERT INTO organisations (name) VALUES ('e2e camera org') RETURNING id")" # `viewer`: the relay route asks for viewer, so seeding the weakest role that # should work shows the camera is watchable without any authority to command. -psql_q "INSERT INTO org_members (org_id, user_id, role) VALUES ('$ORG_ID', '$USER_ID', 'viewer')" >/dev/null +# Serving is for working on the dashboard, which needs to command and to manage +# the org, so it seeds the strongest role instead. +ROLE=viewer; [ "$SERVE" = "1" ] && ROLE=owner +psql_q "INSERT INTO org_members (org_id, user_id, role) VALUES ('$ORG_ID', '$USER_ID', '$ROLE')" >/dev/null FARM_ID="$(psql_q "INSERT INTO farms (name, org_id, edge_proxy_token) VALUES ('e2e camera farm', '$ORG_ID', '$EDGE_PROXY_TOKEN') RETURNING id")" +# Named as printers.toml names it (only the name line is read), so a dashboard +# shows "H2C" and not a test placeholder. +CAM_NAME="$(awk -F'"' -v id="$CAMERA_ID" '/^id *=/ {cur=$2} cur==id && /^name *=/ {print $2; exit}' "$PROXY_DIR/printers.toml")" psql_q "INSERT INTO printers (farm_id, name, vendor, gateway_printer_id) - VALUES ('$FARM_ID', 'camera under test', 'bambu', '$CAMERA_ID')" >/dev/null + VALUES ('$FARM_ID', '${CAM_NAME:-camera under test}', 'bambu', '$CAMERA_ID')" >/dev/null CAM_UUID="$(psql_q "SELECT id FROM printers WHERE farm_id='$FARM_ID' AND gateway_printer_id='$CAMERA_ID'")" -ok "org $ORG_ID, farm $FARM_ID, viewer $USER_ID" +if [ "$SERVE" = "1" ]; then + # Every other printer the gateway will report, or its telemetry is skipped as + # unknown. Only ids and names are read from printers.toml, never credentials. + while read -r pid pname; do + [ -z "$pid" ] || [ "$pid" = "$CAMERA_ID" ] && continue + psql_q "INSERT INTO printers (farm_id, name, vendor, gateway_printer_id) + VALUES ('$FARM_ID', '$pname', 'bambu', '$pid')" >/dev/null + done < <(awk -F'"' '/^id *=/ {id=$2} /^name *=/ {print id, $2}' "$PROXY_DIR/printers.toml") +fi +ok "org $ORG_ID, farm $FARM_ID, $ROLE $USER_ID" # --- 2. backend ------------------------------------------------------------ @@ -289,6 +320,7 @@ run_viewer() { fi } +if [ "$SERVE" != "1" ]; then echo "==> a real WebRTC peer plays the camera through the relay (direct candidates)" run_viewer "direct" 5 @@ -302,6 +334,7 @@ else echo " SKIPPED: relay-only phase - CLOUDFLARE_TURN_KEY_ID / CLOUDFLARE_TURN_API_TOKEN not set." echo " The TURN-relayed path, which the browser is pinned to, is NOT verified by this run." fi +fi # --- 5. the real web console in a real browser ------------------------------- @@ -315,7 +348,8 @@ if [ "$BROWSER" = "1" ]; then NUXT_PUBLIC_WS_BASE="ws://localhost:$PORT" \ SUPABASE_URL="$SUPABASE_URL" \ SUPABASE_KEY="$SUPABASE_PUBLISHABLE_KEY" \ - pnpm exec nuxt dev --host 127.0.0.1 --port "$APP_PORT" > "$WORK/app.log" 2>&1 & + NUXT_PUBLIC_PASSWORD_LOGIN=true \ + pnpm exec nuxt dev --host "$APP_HOST" --port "$APP_PORT" > "$WORK/app.log" 2>&1 & APP_PID=$! for _ in $(seq 1 120); do curl -sf -o /dev/null "http://127.0.0.1:$APP_PORT/login" 2>/dev/null && break @@ -325,6 +359,25 @@ if [ "$BROWSER" = "1" ]; then || { tail -20 "$WORK/app.log" >&2; fail "the web console did not start on :$APP_PORT"; } ok "the web console is up" + if [ "$SERVE" = "1" ]; then + # Written to a file rather than printed: the password is real, if + # throwaway. umask first so it is never briefly readable by anyone else. + LOGIN_FILE="${SERVE_LOGIN_FILE:-/tmp/continuum-dev-login}" + ( umask 077; printf 'email: %s\npassword: %s\n' "$SB_EMAIL" "$SB_PASSWORD" > "$LOGIN_FILE" ) + echo + echo "READY - the dashboard stack is running and will hold until stopped." + echo " web console http://$APP_HOST:$APP_PORT (hot reload on edits in $APP_DIR)" + echo " backend API $BASE" + echo " sign in $LOGIN_FILE (mode 600)" + echo " gateway e2e-cam-gw, farm $FARM_ID, printers: $(psql_q "SELECT string_agg(gateway_printer_id, ', ') FROM printers WHERE farm_id='$FARM_ID'")" + # `sleep & wait` rather than a foreground sleep: bash defers a trapped signal + # until a foreground child exits, but `wait` is interrupted at once. The + # trap turns TERM/INT into a normal exit so the EXIT trap tears it all down. + trap 'exit 143' TERM INT + sleep infinity & + wait $! + fi + echo "==> the real console in real Chrome: sign in, open the fleet, watch the camera" BROWSER_OUT="$(env APP_URL="http://127.0.0.1:$APP_PORT" SUPABASE_E2E_EMAIL="$SB_EMAIL" \ SUPABASE_E2E_PASSWORD="$SB_PASSWORD" CHROME_BIN="$CHROME_BIN" \