From 705336b42234679235399e9b566004969a39c9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iwo=20Strzebo=C5=84ski?= Date: Thu, 29 Oct 2026 10:17:54 +0000 Subject: [PATCH] Add SERVE=1: hold the whole stack up for working on the dashboard Building a dashboard needs the backend, the proxy with its go2rtc, the web console with hot reload and a seeded farm to stay up while it is being edited, and e2e-camera.sh already boots exactly that. SERVE=1 skips every check, seeds the user as owner (the dashboard has to command and manage the org) with every printer in printers.toml, named as that file names them, and holds until SIGTERM or Ctrl-C, when the usual cleanup removes the farm, the processes and the sign-in file. The password is written to a 0600 file under umask 077 and only the path is printed. The web console binds loopback unless APP_HOST says otherwise, because it serves a password sign-in. Verified: signing in through the real form against the live stack shows Live and both printers (the H2C live, the P1P honestly "offline, Never reported" because it was switched off); SIGTERM leaves no process, port, login file or seeded farm behind. --- scripts/e2e-camera.sh | 61 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) 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" \