CLAUDE: Improve service scripts and fix WebSocket plugin conflict

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 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2025-11-29 15:23:41 -06:00
parent 46cf1cc02b
commit b68e3ceacf
5 changed files with 859 additions and 137 deletions

View File

@ -125,6 +125,15 @@ export function useWebSocket() {
try { try {
// Create or reuse socket instance // Create or reuse socket instance
if (!socketInstance) { 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, { socketInstance = io(wsUrl, {
withCredentials: true, // Send cookies automatically withCredentials: true, // Send cookies automatically
autoConnect: false, autoConnect: false,
@ -132,14 +141,17 @@ export function useWebSocket() {
transports: ['websocket', 'polling'], transports: ['websocket', 'polling'],
}) })
console.log('[WebSocket] Socket instance created:', !!socketInstance)
setupEventListeners() setupEventListeners()
} }
console.log('[WebSocket] Calling socketInstance.connect()')
socketInstance.connect() socketInstance.connect()
} catch (err) { } catch (err) {
console.error('[WebSocket] Connection error:', err) console.error('[WebSocket] Connection error:', err)
isConnecting.value = false isConnecting.value = false
connectionError.value = err instanceof Error ? err.message : 'Connection failed' connectionError.value = err instanceof Error ? err.message : String(err)
if (connectionTimeoutId) { if (connectionTimeoutId) {
clearTimeout(connectionTimeoutId) clearTimeout(connectionTimeoutId)
connectionTimeoutId = null connectionTimeoutId = null

View File

@ -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
}
}
}
}
})

View File

@ -1,48 +1,368 @@
#!/bin/bash #!/bin/bash
# Start both backend and frontend services for SBA gameplay testing # Start both backend and frontend services for SBA gameplay testing
# Enhanced with pre-flight checks, health monitoring, and version logging
set -e set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_DIR="$SCRIPT_DIR/logs" 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 # Create logs directory if it doesn't exist
mkdir -p "$LOG_DIR" mkdir -p "$LOG_DIR"
echo "🚀 Starting SBA Gameplay Services..." echo "========================================"
echo "🚀 Starting SBA Gameplay Services"
echo "========================================"
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)..." echo "📡 Starting Backend (FastAPI + Socket.io)..."
cd "$SCRIPT_DIR/backend" cd "$SCRIPT_DIR/backend"
uv run python -m app.main > "$LOG_DIR/backend.log" 2>&1 & uv run python -m app.main > "$LOG_DIR/backend.log" 2>&1 &
BACKEND_PID=$! BACKEND_PID=$!
echo " Backend started with PID: $BACKEND_PID" echo "$BACKEND_PID" > "$LOG_DIR/backend.pid"
echo " Logs: $LOG_DIR/backend.log" echo " PID: $BACKEND_PID"
echo " API: http://localhost:8000" 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 "" echo ""
# Wait for backend to initialize if [ "$BACKEND_READY" = true ]; then
sleep 3 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 & npm run dev > "$LOG_DIR/frontend.log" 2>&1 &
FRONTEND_PID=$! 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 "$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 ""
echo "📊 To view logs:"
echo " Backend: tail -f $LOG_DIR/backend.log" if [ "$FRONTEND_READY" = true ]; then
echo " Frontend: tail -f $LOG_DIR/frontend.log" 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 ""
echo "🛑 To stop services: ./stop-services.sh"

275
status-services.sh Executable file
View File

@ -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

View File

@ -1,12 +1,52 @@
#!/bin/bash #!/bin/bash
# Stop both backend and frontend services # 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)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_DIR="$SCRIPT_DIR/logs" LOG_DIR="$SCRIPT_DIR/logs"
BACKEND_PORT=8000
FRONTEND_PORT=3000
echo "🛑 Stopping SBA Gameplay Services..." # Parse arguments
echo "" 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 # Function to kill process tree
kill_tree() { kill_tree() {
@ -16,7 +56,7 @@ kill_tree() {
# Get all child PIDs # Get all child PIDs
local children=$(pgrep -P "$pid" 2>/dev/null || true) local children=$(pgrep -P "$pid" 2>/dev/null || true)
# Kill children first # Kill children first (depth-first)
for child in $children; do for child in $children; do
kill_tree "$child" "$sig" kill_tree "$child" "$sig"
done done
@ -27,87 +67,211 @@ kill_tree() {
fi fi
} }
# Stop Backend # Function to stop a service gracefully
if [ -f "$LOG_DIR/backend.pid" ]; then stop_service() {
BACKEND_PID=$(cat "$LOG_DIR/backend.pid") local name=$1
if ps -p "$BACKEND_PID" > /dev/null 2>&1; then local pid_file=$2
echo "📡 Stopping Backend (PID: $BACKEND_PID)..." local icon=$3
kill_tree "$BACKEND_PID" TERM
sleep 1 if [ -f "$pid_file" ]; then
# Force kill if still running local pid=$(cat "$pid_file")
if ps -p "$BACKEND_PID" > /dev/null 2>&1; then if ps -p "$pid" > /dev/null 2>&1; then
kill_tree "$BACKEND_PID" KILL 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 fi
echo " ✓ Backend stopped" rm -f "$pid_file"
else else
echo " ⚠ Backend process not found" log " No $name PID file found"
fi fi
rm -f "$LOG_DIR/backend.pid" }
else
echo " ⚠ No backend PID file found" # Stop Backend
fi stop_service "Backend" "$LOG_DIR/backend.pid" "📡"
echo "" log ""
# Stop Frontend # Stop Frontend
if [ -f "$LOG_DIR/frontend.pid" ]; then stop_service "Frontend" "$LOG_DIR/frontend.pid" "🎨"
FRONTEND_PID=$(cat "$LOG_DIR/frontend.pid") log ""
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 ""
# 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) ORPHANS_FOUND=false
BACKEND_PIDS=$(pgrep -f "python.*app\.main" 2>/dev/null || true)
UVICORN_PIDS=$(pgrep -f "uvicorn.*app\.main" 2>/dev/null || true) # Kill all backend processes for this specific app
ALL_BACKEND_PIDS="$BACKEND_PIDS $UVICORN_PIDS" # Use more specific pattern to avoid killing unrelated processes
ALL_BACKEND_PIDS=$(echo "$ALL_BACKEND_PIDS" | tr ' ' '\n' | sort -u | tr '\n' ' ' | xargs) 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 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 for pid in $ALL_BACKEND_PIDS; do
kill_tree "$pid" KILL kill_tree "$pid" KILL
done done
echo " ✓ Killed orphaned backend processes" log " ✓ Killed orphaned backend processes"
fi fi
# Also kill any processes on port 8000 # Kill processes on backend port
PORT_8000_PIDS=$(lsof -ti :8000 2>/dev/null || true) PORT_BACKEND_PIDS=$(lsof -ti :$BACKEND_PORT 2>/dev/null || true)
if [ -n "$PORT_8000_PIDS" ]; then if [ -n "$PORT_BACKEND_PIDS" ]; then
echo " Found processes on port 8000: $PORT_8000_PIDS" ORPHANS_FOUND=true
for pid in $PORT_8000_PIDS; do log " Found processes on port $BACKEND_PORT: $PORT_BACKEND_PIDS"
for pid in $PORT_BACKEND_PIDS; do
kill -9 "$pid" 2>/dev/null || true kill -9 "$pid" 2>/dev/null || true
done done
echo " ✓ Killed processes on port 8000" log " ✓ Killed processes on port $BACKEND_PORT"
fi fi
# Kill all nuxt dev processes in this directory # Kill processes on frontend port
FRONTEND_PIDS=$(pgrep -f "nuxt.*dev" | while read pid; do PORT_FRONTEND_PIDS=$(lsof -ti :$FRONTEND_PORT 2>/dev/null || true)
if ps -p $pid -o args= | grep -q "$SCRIPT_DIR/frontend-sba"; then if [ -n "$PORT_FRONTEND_PIDS" ]; then
echo $pid 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 fi
done) done
if [ -n "$FRONTEND_PIDS" ]; then NUXT_PIDS=$(echo "$NUXT_PIDS" | xargs)
echo " Found orphaned frontend processes: $FRONTEND_PIDS"
for pid in $FRONTEND_PIDS; do 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 kill_tree "$pid" KILL
done done
echo " ✓ Killed orphaned frontend processes" log " ✓ Killed orphaned frontend processes"
fi 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 ""