From b68e3ceacf2b464034de6e9f41d07d2e82ba0ca8 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Sat, 29 Nov 2025 15:23:41 -0600 Subject: [PATCH] CLAUDE: Improve service scripts and fix WebSocket plugin conflict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Service Scripts: - start-services.sh: Add pre-flight checks, health monitoring, --dev/--prod modes, port options, dependency checks, and version logging - stop-services.sh: Add port 3000 cleanup, verification, --quiet/--force flags - status-services.sh: New script for monitoring service status with --watch mode WebSocket: - Remove conflicting socket.client.ts plugin that was interfering with useWebSocket.ts composable (used JWT auth vs cookie auth) - Add debugging logs to useWebSocket.ts to diagnose intermittent connection issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- frontend-sba/composables/useWebSocket.ts | 14 +- frontend-sba/plugins/socket.client.ts | 49 --- start-services.sh | 364 +++++++++++++++++++++-- status-services.sh | 275 +++++++++++++++++ stop-services.sh | 294 ++++++++++++++---- 5 files changed, 859 insertions(+), 137 deletions(-) delete mode 100644 frontend-sba/plugins/socket.client.ts create mode 100755 status-services.sh diff --git a/frontend-sba/composables/useWebSocket.ts b/frontend-sba/composables/useWebSocket.ts index 5786c55..187b2cc 100644 --- a/frontend-sba/composables/useWebSocket.ts +++ b/frontend-sba/composables/useWebSocket.ts @@ -125,6 +125,15 @@ export function useWebSocket() { try { // Create or reuse socket instance if (!socketInstance) { + console.log('[WebSocket] Creating socket instance with URL:', wsUrl) + + if (!io) { + console.error('[WebSocket] ERROR: io function is undefined!') + connectionError.value = 'Socket.io library not loaded' + isConnecting.value = false + return + } + socketInstance = io(wsUrl, { withCredentials: true, // Send cookies automatically autoConnect: false, @@ -132,14 +141,17 @@ export function useWebSocket() { transports: ['websocket', 'polling'], }) + console.log('[WebSocket] Socket instance created:', !!socketInstance) + setupEventListeners() } + console.log('[WebSocket] Calling socketInstance.connect()') socketInstance.connect() } catch (err) { console.error('[WebSocket] Connection error:', err) isConnecting.value = false - connectionError.value = err instanceof Error ? err.message : 'Connection failed' + connectionError.value = err instanceof Error ? err.message : String(err) if (connectionTimeoutId) { clearTimeout(connectionTimeoutId) connectionTimeoutId = null diff --git a/frontend-sba/plugins/socket.client.ts b/frontend-sba/plugins/socket.client.ts deleted file mode 100644 index ba0ab4b..0000000 --- a/frontend-sba/plugins/socket.client.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { Socket } from 'socket.io-client'; -import { io } from 'socket.io-client' - -export default defineNuxtPlugin((nuxtApp) => { - const config = useRuntimeConfig() - let socket: Socket | null = null - - const connect = (token: string) => { - if (socket?.connected) return socket - - socket = io(config.public.wsUrl, { - auth: { token }, - reconnection: true, - reconnectionDelay: 1000, - reconnectionAttempts: 5 - }) - - socket.on('connect', () => { - console.log('WebSocket connected') - }) - - socket.on('disconnect', () => { - console.log('WebSocket disconnected') - }) - - socket.on('connect_error', (error) => { - console.error('WebSocket connection error:', error) - }) - - return socket - } - - const disconnect = () => { - socket?.disconnect() - socket = null - } - - return { - provide: { - socket: { - connect, - disconnect, - get instance() { - return socket - } - } - } - } -}) \ No newline at end of file diff --git a/start-services.sh b/start-services.sh index 1e2c69c..d3b46ee 100755 --- a/start-services.sh +++ b/start-services.sh @@ -1,48 +1,368 @@ #!/bin/bash # Start both backend and frontend services for SBA gameplay testing +# Enhanced with pre-flight checks, health monitoring, and version logging set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LOG_DIR="$SCRIPT_DIR/logs" +# Default configuration +BACKEND_PORT=8000 +FRONTEND_PORT=3000 +BACKEND_HOST="0.0.0.0" +FRONTEND_HOST="0.0.0.0" +HEALTH_CHECK_HOST="localhost" # For curl health checks + +# Environment mode (affects .env file used) +ENV_MODE="dev" # dev, prod, or custom + +# Parse arguments +FORCE_START=false +FRONTEND_TYPE="sba" # Default to SBA frontend + +while [[ $# -gt 0 ]]; do + case $1 in + --force|-f) + FORCE_START=true + shift + ;; + --frontend) + FRONTEND_TYPE="$2" + shift 2 + ;; + --pd) + FRONTEND_TYPE="pd" + shift + ;; + --dev) + ENV_MODE="dev" + shift + ;; + --prod) + ENV_MODE="prod" + shift + ;; + --backend-port) + BACKEND_PORT="$2" + shift 2 + ;; + --frontend-port) + FRONTEND_PORT="$2" + shift 2 + ;; + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --force, -f Force start (stop existing services first)" + echo " --frontend TYPE Start specific frontend (sba or pd)" + echo " --pd Shortcut for --frontend pd" + echo " --dev Development mode - use localhost URLs (default)" + echo " --prod Production mode - use .env production URLs" + echo " --backend-port N Override backend port (default: 8000)" + echo " --frontend-port N Override frontend port (default: 3000)" + echo " --help, -h Show this help message" + echo "" + echo "Environment Modes:" + echo " --dev Sets NUXT_PUBLIC_API_URL=http://localhost:8000" + echo " Sets NUXT_PUBLIC_WS_URL=http://localhost:8000" + echo "" + echo " --prod Uses URLs from frontend-{sba,pd}/.env file" + echo " (e.g., https://gameplay-demo.manticorum.com)" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Validate frontend type +if [[ "$FRONTEND_TYPE" != "sba" && "$FRONTEND_TYPE" != "pd" ]]; then + echo "❌ Invalid frontend type: $FRONTEND_TYPE (must be 'sba' or 'pd')" + exit 1 +fi + +FRONTEND_DIR="$SCRIPT_DIR/frontend-$FRONTEND_TYPE" + # Create logs directory if it doesn't exist mkdir -p "$LOG_DIR" -echo "🚀 Starting SBA Gameplay Services..." +echo "========================================" +echo "🚀 Starting SBA Gameplay Services" +echo "========================================" echo "" -# Start Backend +# ============================================ +# PRE-FLIGHT CHECKS +# ============================================ +echo "🔍 Pre-flight checks..." + +# Check dependencies +check_dependency() { + if ! command -v "$1" &> /dev/null; then + echo "❌ Required tool not found: $1" + echo " Please install $1 before running this script." + exit 1 + fi +} + +check_dependency "uv" +check_dependency "npm" +check_dependency "curl" +echo " ✓ Dependencies available (uv, npm, curl)" + +# Check if frontend directory exists +if [ ! -d "$FRONTEND_DIR" ]; then + echo "❌ Frontend directory not found: $FRONTEND_DIR" + exit 1 +fi +echo " ✓ Frontend directory exists: frontend-$FRONTEND_TYPE" + +# Check if already running +ALREADY_RUNNING=false + +if [ -f "$LOG_DIR/backend.pid" ]; then + EXISTING_PID=$(cat "$LOG_DIR/backend.pid") + if ps -p "$EXISTING_PID" > /dev/null 2>&1; then + ALREADY_RUNNING=true + echo " âš ī¸ Backend already running (PID: $EXISTING_PID)" + fi +fi + +if [ -f "$LOG_DIR/frontend.pid" ]; then + EXISTING_PID=$(cat "$LOG_DIR/frontend.pid") + if ps -p "$EXISTING_PID" > /dev/null 2>&1; then + ALREADY_RUNNING=true + echo " âš ī¸ Frontend already running (PID: $EXISTING_PID)" + fi +fi + +if [ "$ALREADY_RUNNING" = true ]; then + if [ "$FORCE_START" = true ]; then + echo " → Force flag set, stopping existing services..." + "$SCRIPT_DIR/stop-services.sh" + echo "" + else + echo "" + echo "❌ Services already running. Use --force to restart or run ./stop-services.sh first." + exit 1 + fi +fi + +# Check port availability +check_port() { + local port=$1 + local name=$2 + if lsof -ti :"$port" > /dev/null 2>&1; then + echo " âš ī¸ Port $port is in use ($name)" + if [ "$FORCE_START" = true ]; then + echo " → Force flag set, killing processes on port $port..." + lsof -ti :"$port" | xargs kill -9 2>/dev/null || true + sleep 1 + else + echo "" + echo "❌ Port $port is in use. Use --force to kill existing processes." + exit 1 + fi + fi +} + +check_port $BACKEND_PORT "backend" +check_port $FRONTEND_PORT "frontend" +echo " ✓ Ports $BACKEND_PORT and $FRONTEND_PORT available" + +echo "" + +# ============================================ +# VERSION LOGGING (helps debug deployment issues) +# ============================================ +echo "📌 Version Info:" +GIT_COMMIT=$(git -C "$SCRIPT_DIR" rev-parse --short HEAD 2>/dev/null || echo "unknown") +GIT_BRANCH=$(git -C "$SCRIPT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") +GIT_DIRTY=$(git -C "$SCRIPT_DIR" diff --quiet 2>/dev/null && echo "" || echo " (dirty)") +TIMESTAMP=$(date -Iseconds) + +echo " Branch: $GIT_BRANCH" +echo " Commit: $GIT_COMMIT$GIT_DIRTY" +echo " Started: $TIMESTAMP" + +# Log to file for debugging +cat > "$LOG_DIR/startup.log" << EOF +======================================== +Service Startup Log +======================================== +Timestamp: $TIMESTAMP +Git Branch: $GIT_BRANCH +Git Commit: $GIT_COMMIT$GIT_DIRTY +Environment: $ENV_MODE +Frontend: $FRONTEND_TYPE +Backend Port: $BACKEND_PORT +Frontend Port: $FRONTEND_PORT +======================================== +EOF + +echo "" + +# ============================================ +# START BACKEND +# ============================================ echo "📡 Starting Backend (FastAPI + Socket.io)..." cd "$SCRIPT_DIR/backend" uv run python -m app.main > "$LOG_DIR/backend.log" 2>&1 & BACKEND_PID=$! -echo " Backend started with PID: $BACKEND_PID" -echo " Logs: $LOG_DIR/backend.log" -echo " API: http://localhost:8000" +echo "$BACKEND_PID" > "$LOG_DIR/backend.pid" +echo " PID: $BACKEND_PID" +echo " Log: $LOG_DIR/backend.log" + +# Health check with timeout +echo " Waiting for backend to be ready..." +HEALTH_TIMEOUT=30 +HEALTH_COUNT=0 +BACKEND_READY=false + +while [ $HEALTH_COUNT -lt $HEALTH_TIMEOUT ]; do + # Check if process is still running + if ! ps -p $BACKEND_PID > /dev/null 2>&1; then + echo "" + echo "❌ Backend process died during startup!" + echo " Check logs: tail -50 $LOG_DIR/backend.log" + tail -20 "$LOG_DIR/backend.log" 2>/dev/null || true + exit 1 + fi + + # Check health endpoint + if curl -s http://$HEALTH_CHECK_HOST:$BACKEND_PORT/health > /dev/null 2>&1; then + BACKEND_READY=true + break + fi + + # Also accept root endpoint as fallback + if curl -s http://$HEALTH_CHECK_HOST:$BACKEND_PORT/ > /dev/null 2>&1; then + BACKEND_READY=true + break + fi + + sleep 1 + HEALTH_COUNT=$((HEALTH_COUNT + 1)) + printf "." +done + echo "" -# Wait for backend to initialize -sleep 3 +if [ "$BACKEND_READY" = true ]; then + echo " ✓ Backend ready (${HEALTH_COUNT}s)" + echo " URL: http://localhost:$BACKEND_PORT" +else + echo " âš ī¸ Backend health check timed out after ${HEALTH_TIMEOUT}s" + echo " Backend may still be starting. Check logs if issues occur." +fi + +echo "" + +# ============================================ +# START FRONTEND +# ============================================ +echo "🎨 Starting Frontend (Nuxt 3 - $FRONTEND_TYPE)..." +cd "$FRONTEND_DIR" + +# Set environment variables based on mode +if [ "$ENV_MODE" = "dev" ]; then + echo " Mode: Development (localhost)" + export NUXT_PUBLIC_API_URL="http://localhost:$BACKEND_PORT" + export NUXT_PUBLIC_WS_URL="http://localhost:$BACKEND_PORT" + export NUXT_PUBLIC_DISCORD_REDIRECT_URI="http://localhost:$FRONTEND_PORT/auth/callback" +elif [ "$ENV_MODE" = "prod" ]; then + echo " Mode: Production (using .env file)" + # Load .env file if it exists (Nuxt will use these) + if [ -f ".env" ]; then + # Show what URLs will be used + API_URL=$(grep "^NUXT_PUBLIC_API_URL=" .env 2>/dev/null | cut -d= -f2 || echo "not set") + echo " API URL: $API_URL" + else + echo " âš ī¸ No .env file found - using Nuxt defaults" + fi +fi -# Start Frontend -echo "🎨 Starting Frontend (Nuxt 3)..." -cd "$SCRIPT_DIR/frontend-sba" npm run dev > "$LOG_DIR/frontend.log" 2>&1 & FRONTEND_PID=$! -echo " Frontend started with PID: $FRONTEND_PID" -echo " Logs: $LOG_DIR/frontend.log" -echo " URL: http://localhost:3000" -echo "" - -# Save PIDs for stop script -echo "$BACKEND_PID" > "$LOG_DIR/backend.pid" echo "$FRONTEND_PID" > "$LOG_DIR/frontend.pid" +echo " PID: $FRONTEND_PID" +echo " Log: $LOG_DIR/frontend.log" + +# Wait for frontend to be ready +echo " Waiting for frontend to be ready..." +FRONTEND_TIMEOUT=60 +FRONTEND_COUNT=0 +FRONTEND_READY=false + +while [ $FRONTEND_COUNT -lt $FRONTEND_TIMEOUT ]; do + # Check if process is still running + if ! ps -p $FRONTEND_PID > /dev/null 2>&1; then + echo "" + echo "❌ Frontend process died during startup!" + echo " Check logs: tail -50 $LOG_DIR/frontend.log" + tail -20 "$LOG_DIR/frontend.log" 2>/dev/null || true + exit 1 + fi + + # Check if frontend is responding + if curl -s http://$HEALTH_CHECK_HOST:$FRONTEND_PORT > /dev/null 2>&1; then + FRONTEND_READY=true + break + fi + + sleep 1 + FRONTEND_COUNT=$((FRONTEND_COUNT + 1)) + printf "." +done -echo "✅ Services started successfully!" echo "" -echo "📊 To view logs:" -echo " Backend: tail -f $LOG_DIR/backend.log" -echo " Frontend: tail -f $LOG_DIR/frontend.log" + +if [ "$FRONTEND_READY" = true ]; then + echo " ✓ Frontend ready (${FRONTEND_COUNT}s)" + echo " URL: http://localhost:$FRONTEND_PORT" +else + echo " âš ī¸ Frontend health check timed out after ${FRONTEND_TIMEOUT}s" + echo " Frontend may still be compiling. Check logs if issues occur." +fi + +echo "" + +# ============================================ +# SUMMARY +# ============================================ +echo "========================================" +echo "✅ Services Started Successfully!" +echo "========================================" +echo "" +echo "📊 Service Status:" +echo " Backend: http://localhost:$BACKEND_PORT (PID: $BACKEND_PID)" +echo " Frontend: http://localhost:$FRONTEND_PORT (PID: $FRONTEND_PID)" +echo "" + +# Show API URL based on mode +if [ "$ENV_MODE" = "dev" ]; then + echo "🔗 API Configuration (dev mode):" + echo " API URL: http://localhost:$BACKEND_PORT" + echo " WS URL: http://localhost:$BACKEND_PORT" +elif [ "$ENV_MODE" = "prod" ]; then + echo "🔗 API Configuration (prod mode):" + if [ -f "$FRONTEND_DIR/.env" ]; then + grep "^NUXT_PUBLIC_API_URL=" "$FRONTEND_DIR/.env" 2>/dev/null | sed 's/^/ /' || true + grep "^NUXT_PUBLIC_WS_URL=" "$FRONTEND_DIR/.env" 2>/dev/null | sed 's/^/ /' || true + fi +fi +echo "" + +echo "📋 Quick Commands:" +echo " View logs: tail -f $LOG_DIR/backend.log" +echo " tail -f $LOG_DIR/frontend.log" +echo " Check status: ./status-services.sh" +echo " Stop: ./stop-services.sh" +echo " Restart: ./stop-services.sh && ./start-services.sh" +echo "" +echo "📌 Running: $GIT_BRANCH @ $GIT_COMMIT$GIT_DIRTY ($ENV_MODE mode)" echo "" -echo "🛑 To stop services: ./stop-services.sh" diff --git a/status-services.sh b/status-services.sh new file mode 100755 index 0000000..9012cc1 --- /dev/null +++ b/status-services.sh @@ -0,0 +1,275 @@ +#!/bin/bash +# Check status of backend and frontend services +# Shows running state, health, ports, and version info + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LOG_DIR="$SCRIPT_DIR/logs" +BACKEND_PORT=8000 +FRONTEND_PORT=3000 + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Parse arguments +WATCH_MODE=false +JSON_OUTPUT=false + +while [[ $# -gt 0 ]]; do + case $1 in + --watch|-w) + WATCH_MODE=true + shift + ;; + --json|-j) + JSON_OUTPUT=true + shift + ;; + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --watch, -w Continuously monitor (refresh every 2s)" + echo " --json, -j Output in JSON format" + echo " --help, -h Show this help message" + exit 0 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Function to check if a port is in use and get the PID +check_port() { + local port=$1 + lsof -ti :"$port" 2>/dev/null | head -1 +} + +# Function to check HTTP health +check_health() { + local url=$1 + local timeout=${2:-2} + if curl -s --max-time "$timeout" "$url" > /dev/null 2>&1; then + echo "healthy" + else + echo "unhealthy" + fi +} + +# Function to get process uptime +get_uptime() { + local pid=$1 + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + ps -p "$pid" -o etime= 2>/dev/null | xargs + else + echo "N/A" + fi +} + +# Function to get memory usage +get_memory() { + local pid=$1 + if [ -n "$pid" ] && ps -p "$pid" > /dev/null 2>&1; then + ps -p "$pid" -o rss= 2>/dev/null | awk '{printf "%.1f MB", $1/1024}' + else + echo "N/A" + fi +} + +# Main status check function +check_status() { + # Get PIDs from files + BACKEND_PID_FILE="" + FRONTEND_PID_FILE="" + + if [ -f "$LOG_DIR/backend.pid" ]; then + BACKEND_PID_FILE=$(cat "$LOG_DIR/backend.pid") + fi + + if [ -f "$LOG_DIR/frontend.pid" ]; then + FRONTEND_PID_FILE=$(cat "$LOG_DIR/frontend.pid") + fi + + # Check actual running processes + BACKEND_PID_PORT=$(check_port $BACKEND_PORT) + FRONTEND_PID_PORT=$(check_port $FRONTEND_PORT) + + # Determine backend status + BACKEND_RUNNING=false + BACKEND_PID="" + if [ -n "$BACKEND_PID_FILE" ] && ps -p "$BACKEND_PID_FILE" > /dev/null 2>&1; then + BACKEND_RUNNING=true + BACKEND_PID="$BACKEND_PID_FILE" + elif [ -n "$BACKEND_PID_PORT" ]; then + BACKEND_RUNNING=true + BACKEND_PID="$BACKEND_PID_PORT" + fi + + # Determine frontend status + FRONTEND_RUNNING=false + FRONTEND_PID="" + if [ -n "$FRONTEND_PID_FILE" ] && ps -p "$FRONTEND_PID_FILE" > /dev/null 2>&1; then + FRONTEND_RUNNING=true + FRONTEND_PID="$FRONTEND_PID_FILE" + elif [ -n "$FRONTEND_PID_PORT" ]; then + FRONTEND_RUNNING=true + FRONTEND_PID="$FRONTEND_PID_PORT" + fi + + # Check health + BACKEND_HEALTH="stopped" + FRONTEND_HEALTH="stopped" + + if [ "$BACKEND_RUNNING" = true ]; then + BACKEND_HEALTH=$(check_health "http://localhost:$BACKEND_PORT/health") + if [ "$BACKEND_HEALTH" = "unhealthy" ]; then + # Try root endpoint as fallback + BACKEND_HEALTH=$(check_health "http://localhost:$BACKEND_PORT/") + fi + fi + + if [ "$FRONTEND_RUNNING" = true ]; then + FRONTEND_HEALTH=$(check_health "http://localhost:$FRONTEND_PORT") + fi + + # Get version info from startup log + GIT_COMMIT="unknown" + GIT_BRANCH="unknown" + START_TIME="unknown" + ENV_MODE="unknown" + + if [ -f "$LOG_DIR/startup.log" ]; then + GIT_COMMIT=$(grep "Git Commit:" "$LOG_DIR/startup.log" 2>/dev/null | cut -d: -f2 | xargs || echo "unknown") + GIT_BRANCH=$(grep "Git Branch:" "$LOG_DIR/startup.log" 2>/dev/null | cut -d: -f2 | xargs || echo "unknown") + START_TIME=$(grep "Timestamp:" "$LOG_DIR/startup.log" 2>/dev/null | cut -d: -f2- | xargs || echo "unknown") + ENV_MODE=$(grep "Environment:" "$LOG_DIR/startup.log" 2>/dev/null | cut -d: -f2 | xargs || echo "unknown") + fi + + if [ "$JSON_OUTPUT" = true ]; then + # JSON output + cat << EOF +{ + "backend": { + "running": $BACKEND_RUNNING, + "pid": ${BACKEND_PID:-null}, + "port": $BACKEND_PORT, + "health": "$BACKEND_HEALTH", + "uptime": "$(get_uptime "$BACKEND_PID")", + "memory": "$(get_memory "$BACKEND_PID")" + }, + "frontend": { + "running": $FRONTEND_RUNNING, + "pid": ${FRONTEND_PID:-null}, + "port": $FRONTEND_PORT, + "health": "$FRONTEND_HEALTH", + "uptime": "$(get_uptime "$FRONTEND_PID")", + "memory": "$(get_memory "$FRONTEND_PID")" + }, + "version": { + "branch": "$GIT_BRANCH", + "commit": "$GIT_COMMIT", + "mode": "$ENV_MODE", + "started": "$START_TIME" + } +} +EOF + else + # Human-readable output + clear 2>/dev/null || true + + echo "========================================" + echo "📊 SBA Gameplay Services Status" + echo "========================================" + echo "" + + # Backend status + echo -n "📡 Backend: " + if [ "$BACKEND_RUNNING" = true ]; then + if [ "$BACKEND_HEALTH" = "healthy" ]; then + echo -e "${GREEN}● Running${NC} (healthy)" + else + echo -e "${YELLOW}● Running${NC} (unhealthy)" + fi + echo " PID: $BACKEND_PID" + echo " Port: $BACKEND_PORT" + echo " Uptime: $(get_uptime "$BACKEND_PID")" + echo " Memory: $(get_memory "$BACKEND_PID")" + echo " URL: http://localhost:$BACKEND_PORT" + else + echo -e "${RED}○ Stopped${NC}" + fi + echo "" + + # Frontend status + echo -n "🎨 Frontend: " + if [ "$FRONTEND_RUNNING" = true ]; then + if [ "$FRONTEND_HEALTH" = "healthy" ]; then + echo -e "${GREEN}● Running${NC} (healthy)" + else + echo -e "${YELLOW}● Running${NC} (unhealthy)" + fi + echo " PID: $FRONTEND_PID" + echo " Port: $FRONTEND_PORT" + echo " Uptime: $(get_uptime "$FRONTEND_PID")" + echo " Memory: $(get_memory "$FRONTEND_PID")" + echo " URL: http://localhost:$FRONTEND_PORT" + else + echo -e "${RED}○ Stopped${NC}" + fi + echo "" + + # Version info + echo "📌 Version:" + echo " Branch: $GIT_BRANCH" + echo " Commit: $GIT_COMMIT" + echo " Mode: $ENV_MODE" + echo " Started: $START_TIME" + echo "" + + # Overall status + if [ "$BACKEND_RUNNING" = true ] && [ "$FRONTEND_RUNNING" = true ]; then + if [ "$BACKEND_HEALTH" = "healthy" ] && [ "$FRONTEND_HEALTH" = "healthy" ]; then + echo -e "${GREEN}✅ All services healthy${NC}" + else + echo -e "${YELLOW}âš ī¸ Some services unhealthy${NC}" + fi + elif [ "$BACKEND_RUNNING" = true ] || [ "$FRONTEND_RUNNING" = true ]; then + echo -e "${YELLOW}âš ī¸ Partial services running${NC}" + else + echo -e "${RED}❌ All services stopped${NC}" + fi + echo "" + + # Quick commands + echo "📋 Commands:" + if [ "$BACKEND_RUNNING" = false ] || [ "$FRONTEND_RUNNING" = false ]; then + echo " Start: ./start-services.sh" + fi + if [ "$BACKEND_RUNNING" = true ] || [ "$FRONTEND_RUNNING" = true ]; then + echo " Stop: ./stop-services.sh" + echo " Restart: ./stop-services.sh && ./start-services.sh" + fi + echo " Logs: tail -f $LOG_DIR/backend.log" + echo "" + + if [ "$WATCH_MODE" = true ]; then + echo -e "${BLUE}[Refreshing every 2s - Press Ctrl+C to exit]${NC}" + fi + fi +} + +# Main execution +if [ "$WATCH_MODE" = true ]; then + # Watch mode - refresh every 2 seconds + while true; do + check_status + sleep 2 + done +else + check_status +fi diff --git a/stop-services.sh b/stop-services.sh index c18f19d..b87a74c 100755 --- a/stop-services.sh +++ b/stop-services.sh @@ -1,12 +1,52 @@ #!/bin/bash # Stop both backend and frontend services -# Enhanced to kill entire process trees and clean up orphans +# 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 -echo "🛑 Stopping SBA Gameplay Services..." -echo "" +# 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() { @@ -16,7 +56,7 @@ kill_tree() { # Get all child PIDs local children=$(pgrep -P "$pid" 2>/dev/null || true) - # Kill children first + # Kill children first (depth-first) for child in $children; do kill_tree "$child" "$sig" done @@ -27,87 +67,211 @@ kill_tree() { fi } -# Stop Backend -if [ -f "$LOG_DIR/backend.pid" ]; then - BACKEND_PID=$(cat "$LOG_DIR/backend.pid") - if ps -p "$BACKEND_PID" > /dev/null 2>&1; then - echo "📡 Stopping Backend (PID: $BACKEND_PID)..." - kill_tree "$BACKEND_PID" TERM - sleep 1 - # Force kill if still running - if ps -p "$BACKEND_PID" > /dev/null 2>&1; then - kill_tree "$BACKEND_PID" KILL +# 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 - echo " ✓ Backend stopped" + rm -f "$pid_file" else - echo " ⚠ Backend process not found" + log " â„šī¸ No $name PID file found" fi - rm -f "$LOG_DIR/backend.pid" -else - echo " ⚠ No backend PID file found" -fi -echo "" +} + +# Stop Backend +stop_service "Backend" "$LOG_DIR/backend.pid" "📡" +log "" # Stop Frontend -if [ -f "$LOG_DIR/frontend.pid" ]; then - FRONTEND_PID=$(cat "$LOG_DIR/frontend.pid") - if ps -p "$FRONTEND_PID" > /dev/null 2>&1; then - echo "🎨 Stopping Frontend (PID: $FRONTEND_PID)..." - kill_tree "$FRONTEND_PID" TERM - sleep 1 - # Force kill if still running - if ps -p "$FRONTEND_PID" > /dev/null 2>&1; then - kill_tree "$FRONTEND_PID" KILL - fi - echo " ✓ Frontend stopped" - else - echo " ⚠ Frontend process not found" - fi - rm -f "$LOG_DIR/frontend.pid" -else - echo " ⚠ No frontend PID file found" -fi -echo "" +stop_service "Frontend" "$LOG_DIR/frontend.pid" "🎨" +log "" -# Aggressive cleanup: kill ALL related processes by pattern matching -echo "🔍 Checking for any remaining processes..." +# ============================================ +# AGGRESSIVE CLEANUP: Kill orphaned processes +# ============================================ +log "🔍 Checking for orphaned processes..." -# Kill all backend processes for this app (uvicorn OR python -m app.main) -BACKEND_PIDS=$(pgrep -f "python.*app\.main" 2>/dev/null || true) -UVICORN_PIDS=$(pgrep -f "uvicorn.*app\.main" 2>/dev/null || true) -ALL_BACKEND_PIDS="$BACKEND_PIDS $UVICORN_PIDS" -ALL_BACKEND_PIDS=$(echo "$ALL_BACKEND_PIDS" | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs) +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 - echo " Found orphaned backend processes: $ALL_BACKEND_PIDS" + ORPHANS_FOUND=true + log " Found orphaned backend processes: $ALL_BACKEND_PIDS" for pid in $ALL_BACKEND_PIDS; do kill_tree "$pid" KILL done - echo " ✓ Killed orphaned backend processes" + log " ✓ Killed orphaned backend processes" fi -# Also kill any processes on port 8000 -PORT_8000_PIDS=$(lsof -ti :8000 2>/dev/null || true) -if [ -n "$PORT_8000_PIDS" ]; then - echo " Found processes on port 8000: $PORT_8000_PIDS" - for pid in $PORT_8000_PIDS; do +# 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 - echo " ✓ Killed processes on port 8000" + log " ✓ Killed processes on port $BACKEND_PORT" fi -# Kill all nuxt dev processes in this directory -FRONTEND_PIDS=$(pgrep -f "nuxt.*dev" | while read pid; do - if ps -p $pid -o args= | grep -q "$SCRIPT_DIR/frontend-sba"; then - echo $pid +# 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) -if [ -n "$FRONTEND_PIDS" ]; then - echo " Found orphaned frontend processes: $FRONTEND_PIDS" - for pid in $FRONTEND_PIDS; do +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 - echo " ✓ Killed orphaned frontend processes" + log " ✓ Killed orphaned frontend processes" fi -echo "✅ Services stopped successfully!" +# 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 ""