#!/usr/bin/env bash # Starts the FastAPI backend and Vite frontend for local development. # Run from anywhere — paths are resolved relative to this script. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BACKEND_DIR="$SCRIPT_DIR/backend" FRONTEND_DIR="$SCRIPT_DIR/frontend" VENV="$BACKEND_DIR/.venv" # Match whichever ROS_DOMAIN_ID the robot containers are using (default 0). # Override: ROS_DOMAIN_ID=42 ./dev.sh ROS_DOMAIN_ID="${ROS_DOMAIN_ID:-0}" # Hostname of the WebRTC signaling server (default: same host as the page). # Override: VITE_WEBRTC_HOST=rapsbot-v2.local ./dev.sh # Or set VITE_WEBRTC_HOST in frontend/.env.local (see .env.local.example). export VITE_WEBRTC_HOST="${VITE_WEBRTC_HOST:-}" # ── colour helpers ──────────────────────────────────────────────────────────── G='\033[0;32m'; Y='\033[1;33m'; R='\033[0;31m'; NC='\033[0m' info() { echo -e "${G}[webui]${NC} $*"; } warn() { echo -e "${Y}[webui]${NC} $*"; } error() { echo -e "${R}[webui]${NC} $*" >&2; } # ── sanity checks ───────────────────────────────────────────────────────────── if [ -z "${ROS_DISTRO:-}" ]; then error "ROS_DISTRO is not set. Source a ROS setup file first, or rebuild the devcontainer." exit 1 fi if ! command -v node &>/dev/null; then error "Node.js not found. Rebuild the devcontainer to install it (see .devcontainer/Dockerfile)." exit 1 fi # ── backend: venv ───────────────────────────────────────────────────────────── # Prefer the venv baked into the devcontainer image; fall back to a local one. if [ -d "/opt/webui-venv" ]; then VENV="/opt/webui-venv" info "Using devcontainer venv at $VENV" else if [ ! -d "$VENV" ]; then info "Creating Python venv at $VENV..." python3 -m venv "$VENV" fi info "Syncing backend dependencies..." "$VENV/bin/pip" install -q -r "$BACKEND_DIR/requirements.txt" fi # ── frontend: npm deps ──────────────────────────────────────────────────────── if [ ! -d "$FRONTEND_DIR/node_modules" ]; then info "Installing frontend dependencies..." npm --prefix "$FRONTEND_DIR" install fi # ── cleanup on exit ─────────────────────────────────────────────────────────── BACKEND_PID="" FRONTEND_PID="" cleanup() { warn "Shutting down..." [ -n "$BACKEND_PID" ] && kill "$BACKEND_PID" 2>/dev/null || true [ -n "$FRONTEND_PID" ] && kill "$FRONTEND_PID" 2>/dev/null || true wait "$BACKEND_PID" "$FRONTEND_PID" 2>/dev/null || true } trap cleanup EXIT INT TERM # ── start backend ───────────────────────────────────────────────────────────── info "Starting FastAPI backend on :8080 (ROS_DOMAIN_ID=$ROS_DOMAIN_ID)" ( # shellcheck disable=SC1091 set +u # ROS setup.bash references AMENT_TRACE_SETUP_FILES without a default source "/opt/ros/$ROS_DISTRO/setup.bash" export ROS_DOMAIN_ID exec "$VENV/bin/python3" "$BACKEND_DIR/main.py" ) & BACKEND_PID=$! # ── start frontend ──────────────────────────────────────────────────────────── info "Starting Vite dev server on :5173" npm --prefix "$FRONTEND_DIR" run dev & FRONTEND_PID=$! info "Ready — open http://localhost:5173" info "Press Ctrl+C to stop both processes." echo # Exit as soon as either process dies wait -n "$BACKEND_PID" "$FRONTEND_PID"