#!/usr/bin/env bash # Launch FastAPI (internal :8000) and Next.js (public :7860) together. # HF Spaces sends SIGTERM on shutdown; we trap it and forward to children. set -euo pipefail BACKEND_PORT="${BACKEND_PORT:-8000}" FRONTEND_PORT="${FRONTEND_PORT:-7860}" BACKEND_INTERNAL_URL="${BACKEND_INTERNAL_URL:-http://127.0.0.1:${BACKEND_PORT}}" export BACKEND_PORT FRONTEND_PORT BACKEND_INTERNAL_URL cleanup() { echo "[start.sh] caught signal, shutting down…" [[ -n "${BACKEND_PID:-}" ]] && kill -TERM "$BACKEND_PID" 2>/dev/null || true [[ -n "${FRONTEND_PID:-}" ]] && kill -TERM "$FRONTEND_PID" 2>/dev/null || true wait || true exit 0 } trap cleanup SIGINT SIGTERM echo "[start.sh] launching FastAPI on :$BACKEND_PORT" cd /app/backend uv run uvicorn main:app --host 127.0.0.1 --port "$BACKEND_PORT" --workers 1 & BACKEND_PID=$! echo "[start.sh] waiting for FastAPI health check at $BACKEND_INTERNAL_URL/health" for _ in $(seq 1 120); do if curl -fsS "$BACKEND_INTERNAL_URL/health" >/dev/null 2>&1; then echo "[start.sh] FastAPI is ready" break fi if ! kill -0 "$BACKEND_PID" 2>/dev/null; then wait "$BACKEND_PID" || true echo "[start.sh] FastAPI exited before becoming ready" cleanup fi sleep 1 done if ! curl -fsS "$BACKEND_INTERNAL_URL/health" >/dev/null 2>&1; then echo "[start.sh] FastAPI did not become ready in time" cleanup fi echo "[start.sh] launching Next.js on :$FRONTEND_PORT" cd /app/frontend HOSTNAME=0.0.0.0 PORT="$FRONTEND_PORT" npx next start -p "$FRONTEND_PORT" -H 0.0.0.0 & FRONTEND_PID=$! # If either process dies, exit so HF restarts the Space. wait -n "$BACKEND_PID" "$FRONTEND_PID" EXIT_CODE=$? echo "[start.sh] a child exited (code=$EXIT_CODE), shutting down sibling" cleanup