mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 06:11:07 -05:00
feat: Implement Entity Suggestion Manager and Modal components
- Added EntitySuggestionManager.vue to manage entity suggestions and authentication. - Created EntitySuggestionModal.vue for displaying suggestions and adding new entities. - Integrated AuthManager for user authentication within the suggestion modal. - Enhanced signal handling in start-servers.sh for graceful shutdown of servers. - Improved server startup script to ensure proper cleanup and responsiveness to termination signals. - Added documentation for signal handling fixes and usage instructions.
This commit is contained in:
202
shared/docs/development/start-servers-signal-handling-fix.md
Normal file
202
shared/docs/development/start-servers-signal-handling-fix.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# Signal Handling Fix for start-servers.sh
|
||||
|
||||
## Problem Description
|
||||
|
||||
The [`start-servers.sh`](../../scripts/start-servers.sh) script was not properly responding to Ctrl+C (SIGINT) signals, causing the script to continue running even after the user attempted to stop it. This left background server processes running and made it difficult to gracefully shut down the development environment.
|
||||
|
||||
## Root Causes Identified
|
||||
|
||||
1. **Late Signal Trap Registration**: Signal traps were only registered in the `wait_for_servers()` function after all servers had started, leaving a window during startup where Ctrl+C wouldn't work.
|
||||
|
||||
2. **No Signal Handling During Startup**: The entire initialization and server startup process had no signal traps, making the script unresponsive to interruption during these phases.
|
||||
|
||||
3. **Background Process Signal Issues**: Background processes weren't properly configured to receive termination signals from the parent script.
|
||||
|
||||
4. **No Recursive Signal Prevention**: Multiple signal calls could interfere with graceful shutdown.
|
||||
|
||||
5. **Inefficient Signal Detection**: The monitoring loop used longer sleep intervals, making signal response less responsive.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Early Signal Trap Registration
|
||||
|
||||
**Location**: `main()` function, line ~463
|
||||
|
||||
**Before**:
|
||||
```bash
|
||||
main() {
|
||||
print_status "ThrillWiki Server Start Script Starting..."
|
||||
print_status "This script works whether servers are currently running or not."
|
||||
print_status "Project root: $PROJECT_ROOT"
|
||||
|
||||
# Validate project structure
|
||||
validate_project
|
||||
```
|
||||
|
||||
**After**:
|
||||
```bash
|
||||
main() {
|
||||
print_status "ThrillWiki Server Start Script Starting..."
|
||||
print_status "This script works whether servers are currently running or not."
|
||||
print_status "Project root: $PROJECT_ROOT"
|
||||
|
||||
# Set up signal traps EARLY - before any long-running operations
|
||||
print_status "Setting up signal handlers for graceful shutdown..."
|
||||
trap 'graceful_shutdown' INT TERM
|
||||
|
||||
# Validate project structure
|
||||
validate_project
|
||||
```
|
||||
|
||||
### 2. Prevent Recursive Signal Handling
|
||||
|
||||
**Location**: `graceful_shutdown()` function, line ~53
|
||||
|
||||
**Before**:
|
||||
```bash
|
||||
graceful_shutdown() {
|
||||
if [ "$CLEANUP_PERFORMED" = true ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
CLEANUP_PERFORMED=true
|
||||
|
||||
print_warning "Received shutdown signal - performing graceful shutdown..."
|
||||
```
|
||||
|
||||
**After**:
|
||||
```bash
|
||||
graceful_shutdown() {
|
||||
if [ "$CLEANUP_PERFORMED" = true ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
CLEANUP_PERFORMED=true
|
||||
|
||||
print_warning "Received shutdown signal - performing graceful shutdown..."
|
||||
|
||||
# Disable further signal handling to prevent recursive calls
|
||||
trap - INT TERM
|
||||
```
|
||||
|
||||
### 3. Improved Background Process Signal Handling
|
||||
|
||||
**Location**: `start_backend()` and `start_frontend()` functions
|
||||
|
||||
**Backend Changes** (line ~227):
|
||||
```bash
|
||||
uv run python manage.py runserver_plus 8000 --verbosity=2 &
|
||||
BACKEND_PID=$!
|
||||
|
||||
# Make sure the background process can receive signals
|
||||
disown -h "$BACKEND_PID" 2>/dev/null || true
|
||||
```
|
||||
|
||||
**Frontend Changes** (line ~260):
|
||||
```bash
|
||||
pnpm vite --port 5173 --open --host localhost --debug &
|
||||
FRONTEND_PID=$!
|
||||
|
||||
# Make sure the background process can receive signals
|
||||
disown -h "$FRONTEND_PID" 2>/dev/null || true
|
||||
```
|
||||
|
||||
### 4. Streamlined wait_for_servers() Function
|
||||
|
||||
**Location**: `wait_for_servers()` function, line ~528
|
||||
|
||||
**Before**:
|
||||
```bash
|
||||
wait_for_servers() {
|
||||
# Set up signal traps only after servers are successfully running
|
||||
print_status "Setting up signal handlers for graceful shutdown..."
|
||||
trap 'graceful_shutdown' INT TERM
|
||||
|
||||
print_status "🚀 Servers are running! Press Ctrl+C for graceful shutdown."
|
||||
print_status "📋 Backend: http://localhost:8000 | Frontend: http://localhost:5173"
|
||||
|
||||
# Keep the script alive and wait for signals
|
||||
while [ "$CLEANUP_PERFORMED" != true ]; do
|
||||
# Check if both servers are still running
|
||||
if [ -n "$BACKEND_PID" ] && ! kill -0 "$BACKEND_PID" 2>/dev/null; then
|
||||
print_error "Backend server has stopped unexpectedly"
|
||||
graceful_shutdown
|
||||
fi
|
||||
|
||||
if [ -n "$FRONTEND_PID" ] && ! kill -0 "$FRONTEND_PID" 2>/dev/null; then
|
||||
print_error "Frontend server has stopped unexpectedly"
|
||||
graceful_shutdown
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
**After**:
|
||||
```bash
|
||||
wait_for_servers() {
|
||||
print_status "🚀 Servers are running! Press Ctrl+C for graceful shutdown."
|
||||
print_status "📋 Backend: http://localhost:8000 | Frontend: http://localhost:5173"
|
||||
|
||||
# Keep the script alive and wait for signals
|
||||
while [ "$CLEANUP_PERFORMED" != true ]; do
|
||||
# Check if both servers are still running
|
||||
if [ -n "$BACKEND_PID" ] && ! kill -0 "$BACKEND_PID" 2>/dev/null; then
|
||||
print_error "Backend server has stopped unexpectedly"
|
||||
graceful_shutdown
|
||||
break
|
||||
fi
|
||||
|
||||
if [ -n "$FRONTEND_PID" ] && ! kill -0 "$FRONTEND_PID" 2>/dev/null; then
|
||||
print_error "Frontend server has stopped unexpectedly"
|
||||
graceful_shutdown
|
||||
break
|
||||
fi
|
||||
|
||||
# Use shorter sleep and check for signals more frequently
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
## Benefits of the Fix
|
||||
|
||||
1. **Immediate Signal Response**: Ctrl+C now works immediately at any point during script execution, including during startup.
|
||||
|
||||
2. **Proper Cleanup**: All background processes are properly terminated when the script receives a signal.
|
||||
|
||||
3. **No Orphaned Processes**: The `disown -h` command ensures background processes receive signals while preventing shell job control interference.
|
||||
|
||||
4. **Faster Response**: Reduced sleep interval from 2 to 1 second makes signal handling more responsive.
|
||||
|
||||
5. **Robust Error Handling**: Prevents recursive signal calls and ensures cleanup only happens once.
|
||||
|
||||
## Testing
|
||||
|
||||
The fix was validated by:
|
||||
- Running syntax validation: `bash -n shared/scripts/start-servers.sh` (passed)
|
||||
- Ensuring script permissions are correct: `chmod +x shared/scripts/start-servers.sh`
|
||||
|
||||
## Usage
|
||||
|
||||
The script now properly responds to Ctrl+C at any time during execution:
|
||||
|
||||
```bash
|
||||
./shared/scripts/start-servers.sh
|
||||
# Press Ctrl+C at any time for graceful shutdown
|
||||
```
|
||||
|
||||
The script will:
|
||||
1. Display "Received shutdown signal - performing graceful shutdown..."
|
||||
2. Stop the backend server (Django with runserver_plus)
|
||||
3. Stop the frontend server (Vite)
|
||||
4. Clean up PID files
|
||||
5. Exit gracefully
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- Signal traps are set early in the `main()` function before any long-running operations
|
||||
- The `disown -h` command removes background processes from job control while keeping them as child processes that can receive signals
|
||||
- The `trap - INT TERM` command in `graceful_shutdown()` prevents recursive signal handling
|
||||
- The monitoring loop includes explicit `break` statements to exit cleanly after cleanup
|
||||
575
shared/scripts/start-servers.sh
Executable file
575
shared/scripts/start-servers.sh
Executable file
@@ -0,0 +1,575 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ThrillWiki Server Start Script
|
||||
# Stops any running servers, clears caches, runs migrations, and starts both servers
|
||||
# Works whether servers are currently running or not
|
||||
# Usage: ./start-servers.sh
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Global variables for process management
|
||||
BACKEND_PID=""
|
||||
FRONTEND_PID=""
|
||||
CLEANUP_PERFORMED=false
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Script directory and project root
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
BACKEND_DIR="$PROJECT_ROOT/backend"
|
||||
FRONTEND_DIR="$PROJECT_ROOT/frontend"
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Function for graceful shutdown
|
||||
graceful_shutdown() {
|
||||
if [ "$CLEANUP_PERFORMED" = true ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
CLEANUP_PERFORMED=true
|
||||
|
||||
print_warning "Received shutdown signal - performing graceful shutdown..."
|
||||
|
||||
# Disable further signal handling to prevent recursive calls
|
||||
trap - INT TERM
|
||||
|
||||
# Kill backend server if running
|
||||
if [ -n "$BACKEND_PID" ] && kill -0 "$BACKEND_PID" 2>/dev/null; then
|
||||
print_status "Stopping backend server (PID: $BACKEND_PID)..."
|
||||
kill -TERM "$BACKEND_PID" 2>/dev/null || true
|
||||
|
||||
# Wait up to 10 seconds for graceful shutdown
|
||||
local count=0
|
||||
while [ $count -lt 10 ] && kill -0 "$BACKEND_PID" 2>/dev/null; do
|
||||
sleep 1
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
# Force kill if still running
|
||||
if kill -0 "$BACKEND_PID" 2>/dev/null; then
|
||||
print_warning "Force killing backend server..."
|
||||
kill -KILL "$BACKEND_PID" 2>/dev/null || true
|
||||
fi
|
||||
print_success "Backend server stopped"
|
||||
else
|
||||
print_status "Backend server not running or already stopped"
|
||||
fi
|
||||
|
||||
# Kill frontend server if running
|
||||
if [ -n "$FRONTEND_PID" ] && kill -0 "$FRONTEND_PID" 2>/dev/null; then
|
||||
print_status "Stopping frontend server (PID: $FRONTEND_PID)..."
|
||||
kill -TERM "$FRONTEND_PID" 2>/dev/null || true
|
||||
|
||||
# Wait up to 10 seconds for graceful shutdown
|
||||
local count=0
|
||||
while [ $count -lt 10 ] && kill -0 "$FRONTEND_PID" 2>/dev/null; do
|
||||
sleep 1
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
# Force kill if still running
|
||||
if kill -0 "$FRONTEND_PID" 2>/dev/null; then
|
||||
print_warning "Force killing frontend server..."
|
||||
kill -KILL "$FRONTEND_PID" 2>/dev/null || true
|
||||
fi
|
||||
print_success "Frontend server stopped"
|
||||
else
|
||||
print_status "Frontend server not running or already stopped"
|
||||
fi
|
||||
|
||||
# Clear PID files if they exist
|
||||
if [ -f "$PROJECT_ROOT/shared/logs/backend.pid" ]; then
|
||||
rm -f "$PROJECT_ROOT/shared/logs/backend.pid"
|
||||
fi
|
||||
if [ -f "$PROJECT_ROOT/shared/logs/frontend.pid" ]; then
|
||||
rm -f "$PROJECT_ROOT/shared/logs/frontend.pid"
|
||||
fi
|
||||
|
||||
print_success "Graceful shutdown completed"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Function to kill processes by pattern
|
||||
kill_processes() {
|
||||
local pattern="$1"
|
||||
local description="$2"
|
||||
|
||||
print_status "Checking for $description processes..."
|
||||
|
||||
# Find and kill processes
|
||||
local pids=$(pgrep -f "$pattern" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$pids" ]; then
|
||||
print_status "Found $description processes, stopping them..."
|
||||
echo "$pids" | xargs kill -TERM 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
# Force kill if still running
|
||||
local remaining_pids=$(pgrep -f "$pattern" 2>/dev/null || true)
|
||||
if [ -n "$remaining_pids" ]; then
|
||||
print_warning "Force killing remaining $description processes..."
|
||||
echo "$remaining_pids" | xargs kill -KILL 2>/dev/null || true
|
||||
fi
|
||||
|
||||
print_success "$description processes stopped"
|
||||
else
|
||||
print_status "No $description processes found (this is fine)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to clear Django cache
|
||||
clear_django_cache() {
|
||||
print_status "Clearing Django cache..."
|
||||
|
||||
cd "$BACKEND_DIR"
|
||||
|
||||
# Clear Django cache
|
||||
if command -v uv >/dev/null 2>&1; then
|
||||
if ! uv run manage.py clear_cache 2>clear_cache_error.log; then
|
||||
print_error "Django clear_cache command failed:"
|
||||
cat clear_cache_error.log
|
||||
rm -f clear_cache_error.log
|
||||
exit 1
|
||||
else
|
||||
rm -f clear_cache_error.log
|
||||
fi
|
||||
else
|
||||
print_error "uv not found! Please install uv first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove Python cache files
|
||||
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
find . -name "*.pyc" -delete 2>/dev/null || true
|
||||
find . -name "*.pyo" -delete 2>/dev/null || true
|
||||
|
||||
print_success "Django cache cleared"
|
||||
}
|
||||
|
||||
# Function to clear frontend cache
|
||||
clear_frontend_cache() {
|
||||
print_status "Clearing frontend cache..."
|
||||
|
||||
cd "$FRONTEND_DIR"
|
||||
|
||||
# Remove node_modules/.cache if it exists
|
||||
if [ -d "node_modules/.cache" ]; then
|
||||
rm -rf node_modules/.cache
|
||||
print_status "Removed node_modules/.cache"
|
||||
fi
|
||||
|
||||
# Remove .nuxt cache if it exists (for Nuxt projects)
|
||||
if [ -d ".nuxt" ]; then
|
||||
rm -rf .nuxt
|
||||
print_status "Removed .nuxt cache"
|
||||
fi
|
||||
|
||||
# Remove dist/build directories
|
||||
if [ -d "dist" ]; then
|
||||
rm -rf dist
|
||||
print_status "Removed dist directory"
|
||||
fi
|
||||
|
||||
if [ -d "build" ]; then
|
||||
rm -rf build
|
||||
print_status "Removed build directory"
|
||||
fi
|
||||
|
||||
# Clear pnpm cache
|
||||
if command -v pnpm >/dev/null 2>&1; then
|
||||
pnpm store prune 2>/dev/null || print_warning "Could not prune pnpm store"
|
||||
else
|
||||
print_error "pnpm not found! Please install pnpm first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_success "Frontend cache cleared"
|
||||
}
|
||||
|
||||
# Function to run Django migrations
|
||||
run_migrations() {
|
||||
print_status "Running Django migrations..."
|
||||
|
||||
cd "$BACKEND_DIR"
|
||||
|
||||
# Check for pending migrations
|
||||
if uv run python manage.py showmigrations --plan | grep -q "\[ \]"; then
|
||||
print_status "Pending migrations found, applying..."
|
||||
uv run python manage.py migrate
|
||||
print_success "Migrations applied successfully"
|
||||
else
|
||||
print_status "No pending migrations found"
|
||||
fi
|
||||
|
||||
# Run any custom management commands if needed
|
||||
# uv run python manage.py collectstatic --noinput --clear 2>/dev/null || print_warning "collectstatic failed or not needed"
|
||||
}
|
||||
|
||||
# Function to start backend server
|
||||
start_backend() {
|
||||
print_status "Starting Django backend server with runserver_plus (verbose output)..."
|
||||
|
||||
cd "$BACKEND_DIR"
|
||||
|
||||
# Start Django development server with runserver_plus for enhanced features and verbose output
|
||||
print_status "Running: uv run python manage.py runserver_plus 8000 --verbosity=2"
|
||||
uv run python manage.py runserver_plus 8000 --verbosity=2 &
|
||||
BACKEND_PID=$!
|
||||
|
||||
# Make sure the background process can receive signals
|
||||
disown -h "$BACKEND_PID" 2>/dev/null || true
|
||||
|
||||
# Wait a moment and check if it started successfully
|
||||
sleep 3
|
||||
if kill -0 $BACKEND_PID 2>/dev/null; then
|
||||
print_success "Backend server started (PID: $BACKEND_PID)"
|
||||
echo $BACKEND_PID > ../shared/logs/backend.pid
|
||||
else
|
||||
print_error "Failed to start backend server"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to start frontend server
|
||||
start_frontend() {
|
||||
print_status "Starting frontend server with verbose output..."
|
||||
|
||||
cd "$FRONTEND_DIR"
|
||||
|
||||
# Install dependencies if node_modules doesn't exist or package.json is newer
|
||||
if [ ! -d "node_modules" ] || [ "package.json" -nt "node_modules" ]; then
|
||||
print_status "Installing/updating frontend dependencies..."
|
||||
pnpm install
|
||||
fi
|
||||
|
||||
# Start frontend development server using Vite with explicit port, auto-open, and verbose output
|
||||
# --port 5173: Use standard Vite port
|
||||
# --open: Automatically open browser when ready
|
||||
# --host localhost: Ensure it binds to localhost
|
||||
# --debug: Enable debug logging
|
||||
print_status "Starting Vite development server with verbose output and auto-browser opening..."
|
||||
print_status "Running: pnpm vite --port 5173 --open --host localhost --debug"
|
||||
pnpm vite --port 5173 --open --host localhost --debug &
|
||||
FRONTEND_PID=$!
|
||||
|
||||
# Make sure the background process can receive signals
|
||||
disown -h "$FRONTEND_PID" 2>/dev/null || true
|
||||
|
||||
# Wait a moment and check if it started successfully
|
||||
sleep 3
|
||||
if kill -0 $FRONTEND_PID 2>/dev/null; then
|
||||
print_success "Frontend server started (PID: $FRONTEND_PID) - browser should open automatically"
|
||||
echo $FRONTEND_PID > ../shared/logs/frontend.pid
|
||||
else
|
||||
print_error "Failed to start frontend server"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to detect operating system
|
||||
detect_os() {
|
||||
case "$(uname -s)" in
|
||||
Darwin*) echo "macos";;
|
||||
Linux*) echo "linux";;
|
||||
*) echo "unknown";;
|
||||
esac
|
||||
}
|
||||
|
||||
# Function to open browser on the appropriate OS
|
||||
open_browser() {
|
||||
local url="$1"
|
||||
local os=$(detect_os)
|
||||
|
||||
print_status "Opening browser to $url..."
|
||||
|
||||
case "$os" in
|
||||
"macos")
|
||||
if command -v open >/dev/null 2>&1; then
|
||||
open "$url" 2>/dev/null || print_warning "Failed to open browser automatically"
|
||||
else
|
||||
print_warning "Cannot open browser: 'open' command not available"
|
||||
fi
|
||||
;;
|
||||
"linux")
|
||||
if command -v xdg-open >/dev/null 2>&1; then
|
||||
xdg-open "$url" 2>/dev/null || print_warning "Failed to open browser automatically"
|
||||
else
|
||||
print_warning "Cannot open browser: 'xdg-open' command not available"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
print_warning "Cannot open browser automatically: Unsupported operating system"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Function to verify frontend is responding (simplified since port is known)
|
||||
verify_frontend_ready() {
|
||||
local frontend_url="http://localhost:5173"
|
||||
local max_checks=15
|
||||
local check=0
|
||||
|
||||
print_status "Verifying frontend server is responding at $frontend_url..."
|
||||
|
||||
while [ $check -lt $max_checks ]; do
|
||||
local response_code=$(curl -s -o /dev/null -w "%{http_code}" "$frontend_url" 2>/dev/null)
|
||||
if [ "$response_code" = "200" ] || [ "$response_code" = "301" ] || [ "$response_code" = "302" ] || [ "$response_code" = "404" ]; then
|
||||
print_success "Frontend server is responding (HTTP $response_code)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ $((check % 3)) -eq 0 ]; then
|
||||
print_status "Waiting for frontend to respond... (attempt $((check + 1))/$max_checks)"
|
||||
fi
|
||||
sleep 2
|
||||
check=$((check + 1))
|
||||
done
|
||||
|
||||
print_warning "Frontend may still be starting up"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Function to verify servers are responding
|
||||
verify_servers_ready() {
|
||||
print_status "Verifying both servers are responding..."
|
||||
|
||||
# Check backend
|
||||
local backend_ready=false
|
||||
local frontend_ready=false
|
||||
local max_checks=10
|
||||
local check=0
|
||||
|
||||
while [ $check -lt $max_checks ]; do
|
||||
# Check backend
|
||||
if [ "$backend_ready" = false ]; then
|
||||
local backend_response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:8000" 2>/dev/null)
|
||||
if [ "$backend_response" = "200" ] || [ "$backend_response" = "301" ] || [ "$backend_response" = "302" ] || [ "$backend_response" = "404" ]; then
|
||||
print_success "Backend server is responding (HTTP $backend_response)"
|
||||
backend_ready=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check frontend
|
||||
if [ "$frontend_ready" = false ]; then
|
||||
local frontend_response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:5173" 2>/dev/null)
|
||||
if [ "$frontend_response" = "200" ] || [ "$frontend_response" = "301" ] || [ "$frontend_response" = "302" ] || [ "$frontend_response" = "404" ]; then
|
||||
print_success "Frontend server is responding (HTTP $frontend_response)"
|
||||
frontend_ready=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Both ready?
|
||||
if [ "$backend_ready" = true ] && [ "$frontend_ready" = true ]; then
|
||||
print_success "Both servers are responding!"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
check=$((check + 1))
|
||||
done
|
||||
|
||||
# Show status of what's working
|
||||
if [ "$backend_ready" = true ]; then
|
||||
print_success "Backend is ready at http://localhost:8000"
|
||||
else
|
||||
print_warning "Backend may still be starting up"
|
||||
fi
|
||||
|
||||
if [ "$frontend_ready" = true ]; then
|
||||
print_success "Frontend is ready at http://localhost:5173"
|
||||
else
|
||||
print_warning "Frontend may still be starting up"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to create logs directory if it doesn't exist
|
||||
ensure_logs_dir() {
|
||||
local logs_dir="$PROJECT_ROOT/shared/logs"
|
||||
if [ ! -d "$logs_dir" ]; then
|
||||
mkdir -p "$logs_dir"
|
||||
print_status "Created logs directory: $logs_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to validate project structure
|
||||
validate_project() {
|
||||
if [ ! -d "$BACKEND_DIR" ]; then
|
||||
print_error "Backend directory not found: $BACKEND_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$FRONTEND_DIR" ]; then
|
||||
print_error "Frontend directory not found: $FRONTEND_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$BACKEND_DIR/manage.py" ]; then
|
||||
print_error "Django manage.py not found in: $BACKEND_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$FRONTEND_DIR/package.json" ]; then
|
||||
print_error "Frontend package.json not found in: $FRONTEND_DIR"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to kill processes using specific ports
|
||||
kill_port_processes() {
|
||||
local port="$1"
|
||||
local description="$2"
|
||||
|
||||
print_status "Checking for processes using port $port ($description)..."
|
||||
|
||||
# Find processes using the specific port
|
||||
local pids=$(lsof -ti :$port 2>/dev/null || true)
|
||||
|
||||
if [ -n "$pids" ]; then
|
||||
print_warning "Found processes using port $port, killing them..."
|
||||
echo "$pids" | xargs kill -TERM 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
# Force kill if still running
|
||||
local remaining_pids=$(lsof -ti :$port 2>/dev/null || true)
|
||||
if [ -n "$remaining_pids" ]; then
|
||||
print_warning "Force killing remaining processes on port $port..."
|
||||
echo "$remaining_pids" | xargs kill -KILL 2>/dev/null || true
|
||||
fi
|
||||
|
||||
print_success "Port $port cleared"
|
||||
else
|
||||
print_status "Port $port is available"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check and clear required ports
|
||||
check_and_clear_ports() {
|
||||
print_status "Checking and clearing required ports..."
|
||||
|
||||
# Kill processes using our specific ports
|
||||
kill_port_processes 8000 "Django backend"
|
||||
kill_port_processes 5173 "Frontend Vite"
|
||||
}
|
||||
|
||||
# Main execution function
|
||||
main() {
|
||||
print_status "ThrillWiki Server Start Script Starting..."
|
||||
print_status "This script works whether servers are currently running or not."
|
||||
print_status "Project root: $PROJECT_ROOT"
|
||||
|
||||
# Set up signal traps EARLY - before any long-running operations
|
||||
print_status "Setting up signal handlers for graceful shutdown..."
|
||||
trap 'graceful_shutdown' INT TERM
|
||||
|
||||
# Validate project structure
|
||||
validate_project
|
||||
|
||||
# Ensure logs directory exists
|
||||
ensure_logs_dir
|
||||
|
||||
# Check and clear ports
|
||||
check_and_clear_ports
|
||||
|
||||
# Kill existing server processes (if any)
|
||||
print_status "=== Stopping Any Running Servers ==="
|
||||
print_status "Note: It's perfectly fine if no servers are currently running"
|
||||
kill_processes "manage.py runserver" "Django backend"
|
||||
kill_processes "pnpm.*dev\|npm.*dev\|yarn.*dev\|node.*dev" "Frontend development"
|
||||
kill_processes "uvicorn\|gunicorn" "Python web servers"
|
||||
|
||||
# Clear caches
|
||||
print_status "=== Clearing Caches ==="
|
||||
clear_django_cache
|
||||
clear_frontend_cache
|
||||
|
||||
# Run migrations
|
||||
print_status "=== Running Migrations ==="
|
||||
run_migrations
|
||||
|
||||
# Start servers
|
||||
print_status "=== Starting Servers ==="
|
||||
|
||||
# Start backend first
|
||||
if start_backend; then
|
||||
print_success "Backend server is running"
|
||||
else
|
||||
print_error "Failed to start backend server"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Start frontend
|
||||
if start_frontend; then
|
||||
print_success "Frontend server is running"
|
||||
else
|
||||
print_error "Failed to start frontend server"
|
||||
print_status "Backend server is still running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify servers are responding
|
||||
print_status "=== Verifying Servers ==="
|
||||
verify_servers_ready
|
||||
|
||||
# Final status
|
||||
print_status "=== Server Status ==="
|
||||
print_success "✅ Backend server: http://localhost:8000 (Django with runserver_plus)"
|
||||
print_success "✅ Frontend server: http://localhost:5173 (Vite with verbose output)"
|
||||
print_status "🌐 Browser should have opened automatically via Vite --open"
|
||||
print_status "🔧 To stop servers, use: kill \$(cat $PROJECT_ROOT/shared/logs/backend.pid) \$(cat $PROJECT_ROOT/shared/logs/frontend.pid)"
|
||||
print_status "📋 Both servers are running with verbose output directly in your terminal"
|
||||
|
||||
print_success "🚀 All servers started successfully with full verbose output!"
|
||||
|
||||
# Keep the script running and wait for signals
|
||||
wait_for_servers
|
||||
}
|
||||
|
||||
# Wait for servers function to keep script running and handle signals
|
||||
wait_for_servers() {
|
||||
print_status "🚀 Servers are running! Press Ctrl+C for graceful shutdown."
|
||||
print_status "📋 Backend: http://localhost:8000 | Frontend: http://localhost:5173"
|
||||
|
||||
# Keep the script alive and wait for signals
|
||||
while [ "$CLEANUP_PERFORMED" != true ]; do
|
||||
# Check if both servers are still running
|
||||
if [ -n "$BACKEND_PID" ] && ! kill -0 "$BACKEND_PID" 2>/dev/null; then
|
||||
print_error "Backend server has stopped unexpectedly"
|
||||
graceful_shutdown
|
||||
break
|
||||
fi
|
||||
|
||||
if [ -n "$FRONTEND_PID" ] && ! kill -0 "$FRONTEND_PID" 2>/dev/null; then
|
||||
print_error "Frontend server has stopped unexpectedly"
|
||||
graceful_shutdown
|
||||
break
|
||||
fi
|
||||
|
||||
# Use shorter sleep and check for signals more frequently
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
# Run main function (no traps set up initially)
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user