mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 12:51:09 -05:00
- 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.
575 lines
18 KiB
Bash
Executable File
575 lines
18 KiB
Bash
Executable File
#!/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 "$@" |