#!/bin/bash # Stop both backend and frontend services # Enhanced to kill entire process trees, clean up orphans, and verify shutdown SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LOG_DIR="$SCRIPT_DIR/logs" BACKEND_PORT=8000 FRONTEND_PORT=3000 # Parse arguments QUIET_MODE=false FORCE_MODE=false while [[ $# -gt 0 ]]; do case $1 in --quiet|-q) QUIET_MODE=true shift ;; --force|-f) FORCE_MODE=true shift ;; --help|-h) echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --quiet, -q Minimal output" echo " --force, -f Skip graceful shutdown, kill immediately" echo " --help, -h Show this help message" exit 0 ;; *) echo "Unknown option: $1" exit 1 ;; esac done log() { if [ "$QUIET_MODE" = false ]; then echo "$@" fi } log "========================================" log "🛑 Stopping SBA Gameplay Services" log "========================================" log "" # Function to kill process tree kill_tree() { local pid=$1 local sig=${2:-TERM} # Get all child PIDs local children=$(pgrep -P "$pid" 2>/dev/null || true) # Kill children first (depth-first) for child in $children; do kill_tree "$child" "$sig" done # Kill the parent if ps -p "$pid" > /dev/null 2>&1; then kill -"$sig" "$pid" 2>/dev/null || true fi } # Function to stop a service gracefully stop_service() { local name=$1 local pid_file=$2 local icon=$3 if [ -f "$pid_file" ]; then local pid=$(cat "$pid_file") if ps -p "$pid" > /dev/null 2>&1; then log "$icon Stopping $name (PID: $pid)..." if [ "$FORCE_MODE" = true ]; then # Force mode: kill immediately kill_tree "$pid" KILL log " ✓ $name killed (force mode)" else # Graceful mode: SIGTERM first, then SIGKILL kill_tree "$pid" TERM # Wait up to 5 seconds for graceful shutdown local wait_count=0 while [ $wait_count -lt 5 ]; do if ! ps -p "$pid" > /dev/null 2>&1; then break fi sleep 1 wait_count=$((wait_count + 1)) done # Force kill if still running if ps -p "$pid" > /dev/null 2>&1; then log " → Graceful shutdown timed out, forcing..." kill_tree "$pid" KILL sleep 1 fi if ps -p "$pid" > /dev/null 2>&1; then log " âš ī¸ $name may still be running" else log " ✓ $name stopped" fi fi else log " âš ī¸ $name process not found (stale PID file)" fi rm -f "$pid_file" else log " â„šī¸ No $name PID file found" fi } # Stop Backend stop_service "Backend" "$LOG_DIR/backend.pid" "📡" log "" # Stop Frontend stop_service "Frontend" "$LOG_DIR/frontend.pid" "🎨" log "" # ============================================ # AGGRESSIVE CLEANUP: Kill orphaned processes # ============================================ log "🔍 Checking for orphaned processes..." ORPHANS_FOUND=false # Kill all backend processes for this specific app # Use more specific pattern to avoid killing unrelated processes BACKEND_PATTERN="python.*app\.main" BACKEND_PIDS=$(pgrep -f "$BACKEND_PATTERN" 2>/dev/null || true) # Also check for uvicorn processes UVICORN_PATTERN="uvicorn.*app\.main" UVICORN_PIDS=$(pgrep -f "$UVICORN_PATTERN" 2>/dev/null || true) # Combine and deduplicate ALL_BACKEND_PIDS=$(echo "$BACKEND_PIDS $UVICORN_PIDS" | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs) if [ -n "$ALL_BACKEND_PIDS" ]; then ORPHANS_FOUND=true log " Found orphaned backend processes: $ALL_BACKEND_PIDS" for pid in $ALL_BACKEND_PIDS; do kill_tree "$pid" KILL done log " ✓ Killed orphaned backend processes" fi # Kill processes on backend port PORT_BACKEND_PIDS=$(lsof -ti :$BACKEND_PORT 2>/dev/null || true) if [ -n "$PORT_BACKEND_PIDS" ]; then ORPHANS_FOUND=true log " Found processes on port $BACKEND_PORT: $PORT_BACKEND_PIDS" for pid in $PORT_BACKEND_PIDS; do kill -9 "$pid" 2>/dev/null || true done log " ✓ Killed processes on port $BACKEND_PORT" fi # Kill processes on frontend port PORT_FRONTEND_PIDS=$(lsof -ti :$FRONTEND_PORT 2>/dev/null || true) if [ -n "$PORT_FRONTEND_PIDS" ]; then ORPHANS_FOUND=true log " Found processes on port $FRONTEND_PORT: $PORT_FRONTEND_PIDS" for pid in $PORT_FRONTEND_PIDS; do kill -9 "$pid" 2>/dev/null || true done log " ✓ Killed processes on port $FRONTEND_PORT" fi # Kill nuxt dev processes specifically for this project # Check if process command line contains our project path NUXT_PIDS="" for pid in $(pgrep -f "nuxt.*dev" 2>/dev/null || true); do # Verify this is for our project by checking cwd or command if ps -p "$pid" -o args= 2>/dev/null | grep -q "$SCRIPT_DIR" 2>/dev/null; then NUXT_PIDS="$NUXT_PIDS $pid" fi done NUXT_PIDS=$(echo "$NUXT_PIDS" | xargs) if [ -n "$NUXT_PIDS" ]; then ORPHANS_FOUND=true log " Found orphaned frontend processes: $NUXT_PIDS" for pid in $NUXT_PIDS; do kill_tree "$pid" KILL done log " ✓ Killed orphaned frontend processes" fi # Kill any node processes that might be related to our frontend # Be more conservative here - only kill if definitely ours NODE_PIDS="" for pid in $(pgrep -f "node.*$SCRIPT_DIR/frontend" 2>/dev/null || true); do NODE_PIDS="$NODE_PIDS $pid" done NODE_PIDS=$(echo "$NODE_PIDS" | xargs) if [ -n "$NODE_PIDS" ]; then ORPHANS_FOUND=true log " Found orphaned node processes: $NODE_PIDS" for pid in $NODE_PIDS; do kill -9 "$pid" 2>/dev/null || true done log " ✓ Killed orphaned node processes" fi if [ "$ORPHANS_FOUND" = false ]; then log " ✓ No orphaned processes found" fi log "" # ============================================ # VERIFICATION: Confirm services are stopped # ============================================ log "🔍 Verification..." # Give processes a moment to fully release ports sleep 1 VERIFICATION_OK=true # Check backend port if lsof -ti :$BACKEND_PORT > /dev/null 2>&1; then log " âš ī¸ Port $BACKEND_PORT still in use!" VERIFICATION_OK=false else log " ✓ Port $BACKEND_PORT is free" fi # Check frontend port if lsof -ti :$FRONTEND_PORT > /dev/null 2>&1; then log " âš ī¸ Port $FRONTEND_PORT still in use!" VERIFICATION_OK=false else log " ✓ Port $FRONTEND_PORT is free" fi # Check for any remaining processes REMAINING_BACKEND=$(pgrep -f "$BACKEND_PATTERN" 2>/dev/null || true) if [ -n "$REMAINING_BACKEND" ]; then log " âš ī¸ Backend processes still running: $REMAINING_BACKEND" VERIFICATION_OK=false fi log "" # ============================================ # SUMMARY # ============================================ if [ "$VERIFICATION_OK" = true ]; then log "========================================" log "✅ Services Stopped Successfully!" log "========================================" else log "========================================" log "âš ī¸ Services Stopped (with warnings)" log "========================================" log "" log "Some processes may still be running. Try:" log " $0 --force" log "" log "Or manually check:" log " lsof -ti :$BACKEND_PORT" log " lsof -ti :$FRONTEND_PORT" fi log ""