#!/usr/bin/env bash set -u if (( BASH_VERSINFO[0] < 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 3) )); then echo "[ERROR] start-all.sh requires Bash 4.3 or newer." >&2 exit 1 fi SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" BACKEND_SCRIPT="$SCRIPT_DIR/start-backend.sh" FRONTEND_SCRIPT="$SCRIPT_DIR/start-reactflow.sh" if [[ ! -x "$BACKEND_SCRIPT" ]]; then echo "[ERROR] Backend start script is missing or not executable:" >&2 echo " $BACKEND_SCRIPT" >&2 exit 1 fi if [[ ! -x "$FRONTEND_SCRIPT" ]]; then echo "[ERROR] Frontend start script is missing or not executable:" >&2 echo " $FRONTEND_SCRIPT" >&2 exit 1 fi BACKEND_PID="" FRONTEND_PID="" signal_process_group() { local signal="$1" local pid="$2" [[ -n "$pid" ]] || return 0 kill "-$signal" -- "-$pid" 2>/dev/null || kill "-$signal" "$pid" 2>/dev/null || true } process_group_is_running() { local pid="$1" [[ -n "$pid" ]] && kill -0 -- "-$pid" 2>/dev/null } cleanup() { local backend_cleared=false local force_kill=false local frontend_cleared=false local launcher_pid=$$ local timer_pid trap '' INT TERM HUP trap - EXIT trap 'force_kill=true' ALRM signal_process_group TERM "$BACKEND_PID" signal_process_group TERM "$FRONTEND_PID" ( sleep 5 kill -ALRM "$launcher_pid" 2>/dev/null || true ) & timer_pid=$! if [[ -n "$BACKEND_PID" ]]; then wait "$BACKEND_PID" 2>/dev/null || true fi if [[ "$force_kill" == false && -n "$FRONTEND_PID" ]]; then wait "$FRONTEND_PID" 2>/dev/null || true fi while [[ "$force_kill" == false ]]; do if [[ "$backend_cleared" == false ]] && ! process_group_is_running "$BACKEND_PID"; then backend_cleared=true fi if [[ "$frontend_cleared" == false ]] && ! process_group_is_running "$FRONTEND_PID"; then frontend_cleared=true fi if [[ "$backend_cleared" == true && "$frontend_cleared" == true ]]; then break fi sleep 0.1 done kill -KILL "$timer_pid" 2>/dev/null || true wait "$timer_pid" 2>/dev/null || true if [[ "$force_kill" == true ]]; then if [[ "$backend_cleared" == false ]] && ! process_group_is_running "$BACKEND_PID"; then backend_cleared=true fi if [[ "$frontend_cleared" == false ]] && ! process_group_is_running "$FRONTEND_PID"; then frontend_cleared=true fi if [[ "$backend_cleared" == false ]]; then signal_process_group KILL "$BACKEND_PID" fi if [[ "$frontend_cleared" == false ]]; then signal_process_group KILL "$FRONTEND_PID" fi fi if [[ -n "$BACKEND_PID" ]]; then wait "$BACKEND_PID" 2>/dev/null || true fi if [[ -n "$FRONTEND_PID" ]]; then wait "$FRONTEND_PID" 2>/dev/null || true fi trap - ALRM } trap cleanup EXIT trap 'exit 130' INT trap 'exit 143' TERM trap 'exit 129' HUP echo "Starting FastAPI and ReactFlow..." echo "Press Ctrl+C to stop both services." echo set -m "$BACKEND_SCRIPT" & BACKEND_PID=$! "$FRONTEND_SCRIPT" & FRONTEND_PID=$! set +m wait -n EXIT_CODE=$? exit "$EXIT_CODE"