diff --git a/scripts/start-dev.sh b/scripts/start-dev.sh index 41169c4..b442162 100755 --- a/scripts/start-dev.sh +++ b/scripts/start-dev.sh @@ -38,6 +38,32 @@ resolve_node_toolchain() { command -v npm >/dev/null 2>&1 } +load_root_env() { + local env_file="$REPO_ROOT/.env" + if [ ! -f "$env_file" ]; then + return 0 + fi + set -a + # shellcheck disable=SC1090 + . "$env_file" + set +a +} + +resolve_database_url() { + if [ -n "${DATABASE_URL:-}" ]; then + printf '%s' "$DATABASE_URL" + return 0 + fi + + local user="${POSTGRES_USER:-postgres}" + local password="${POSTGRES_PASSWORD:-}" + local db="${POSTGRES_DB:-daagbox}" + [ -z "$user" ] && user=postgres + [ -z "$password" ] && password=postgres + [ -z "$db" ] && db=daagbox + printf 'postgresql://%s:%s@localhost:5432/%s?schema=public' "$user" "$password" "$db" +} + check_dev_env() { local env_file="$REPO_ROOT/.env" if [ ! -f "$env_file" ]; then @@ -91,38 +117,84 @@ echo "========================================" echo "Preparing to (re)start services..." require_node_toolchain +load_root_env +export DATABASE_URL="$(resolve_database_url)" check_dev_env # Clean up processes running on ports +stop_process_tree() { + local pid=$1 + [ -z "$pid" ] && return 0 + if ! kill -0 "$pid" 2>/dev/null; then + return 0 + fi + + local child + for child in $(pgrep -P "$pid" 2>/dev/null || true); do + stop_process_tree "$child" + done + + kill -TERM "$pid" 2>/dev/null || true +} + +stop_process_group() { + local pgid=$1 + [ -z "$pgid" ] && return 0 + kill -TERM "-$pgid" 2>/dev/null || true +} + +force_stop_process_group() { + local pgid=$1 + [ -z "$pgid" ] && return 0 + kill -KILL "-$pgid" 2>/dev/null || true +} + cleanup_port() { local port=$1 if command -v lsof >/dev/null 2>&1; then - local pid=$(lsof -t -i:$port) - if [ ! -z "$pid" ]; then - echo "Port $port is currently in use by PID $pid. Stopping process..." - kill -9 $pid 2>/dev/null + local pids + pids=$(lsof -t -i:"$port" 2>/dev/null || true) + if [ -n "$pids" ]; then + echo "Port $port is currently in use. Stopping process(es)..." + local pid + for pid in $pids; do + stop_process_tree "$pid" + done + sleep 0.3 + for pid in $pids; do + kill -KILL "$pid" 2>/dev/null || true + done fi elif command -v fuser >/dev/null 2>&1; then echo "Port $port is currently in use. Stopping process..." - fuser -k $port/tcp 2>/dev/null + fuser -k "$port"/tcp 2>/dev/null fi } -cleanup_port $SERVER_PORT -cleanup_port $CLIENT_PORT +BACKEND_PGID="" +CLIENT_PGID="" -# Clean exit handler cleanup_all() { echo "" echo "Stopping all dev servers..." - # Kill all child jobs - kill $(jobs -p) 2>/dev/null + + stop_process_group "$BACKEND_PGID" + stop_process_group "$CLIENT_PGID" + sleep 0.5 + force_stop_process_group "$BACKEND_PGID" + force_stop_process_group "$CLIENT_PGID" + + cleanup_port "$SERVER_PORT" + cleanup_port "$CLIENT_PORT" exit 0 } # Trap termination signals trap cleanup_all SIGINT SIGTERM EXIT +cleanup_port "$SERVER_PORT" +cleanup_port "$CLIENT_PORT" + # Manage PostgreSQL Docker container if command -v docker >/dev/null 2>&1; then echo "Checking PostgreSQL Docker container (postgres-daagbox)..." @@ -189,8 +261,9 @@ if [ ! -d node_modules ]; then echo "Error: server/node_modules missing. Run: cd server && npm ci" exit 1 fi -npm run dev & +setsid npm run dev & BACKEND_PID=$! +BACKEND_PGID=$BACKEND_PID cd "$REPO_ROOT" || exit 1 # Sleep briefly to let server start up @@ -205,7 +278,9 @@ echo "Starting frontend dev server..." cd "$REPO_ROOT/client" || exit 1 if [ ! -d node_modules ]; then echo "Error: client/node_modules missing. Run: cd client && npm ci" - kill "$BACKEND_PID" 2>/dev/null + stop_process_group "$BACKEND_PGID" + sleep 0.3 + force_stop_process_group "$BACKEND_PGID" exit 1 fi # Vite 6+ via plugin-react 4; refresh lockfile after package.json changes @@ -213,14 +288,17 @@ if ! node -e "require.resolve('vite/package.json')" 2>/dev/null; then echo "Client dependencies incomplete — running npm ci..." npm ci || exit 1 fi -npm run dev & +setsid npm run dev & CLIENT_PID=$! +CLIENT_PGID=$CLIENT_PID cd "$REPO_ROOT" || exit 1 sleep 1.5 if ! kill -0 "$CLIENT_PID" 2>/dev/null; then echo "Error: Frontend dev server exited immediately. Check client logs above." - kill "$BACKEND_PID" 2>/dev/null + stop_process_group "$BACKEND_PGID" + sleep 0.3 + force_stop_process_group "$BACKEND_PGID" exit 1 fi diff --git a/server/package-lock.json b/server/package-lock.json index d884969..57468f1 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "server", "version": "1.0.0", + "hasInstallScript": true, "dependencies": { "@prisma/client": "^5.10.2", "@simplewebauthn/server": "^9.0.3",