#!/bin/bash # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration BACKEND_PORT=8000 FRONTEND_PORT=3000 BACKEND_DIR="backend" FRONTEND_DIR="frontend" LOG_DIR="logs" # Create logs directory if it doesn't exist mkdir -p "$LOG_DIR" # Log files BACKEND_LOG="$LOG_DIR/backend.log" FRONTEND_LOG="$LOG_DIR/frontend.log" # Function to print colored messages print_info() { echo -e "${BLUE}ℹ${NC} $1" } print_success() { echo -e "${GREEN}✓${NC} $1" } print_warning() { echo -e "${YELLOW}⚠${NC} $1" } print_error() { echo -e "${RED}✗${NC} $1" } # Function to kill background processes on exit cleanup() { echo "" print_info "Stopping all services..." # Kill all background jobs jobs -p | xargs -r kill 2>/dev/null # Wait a moment for graceful shutdown sleep 1 # Force kill if still running jobs -p | xargs -r kill -9 2>/dev/null print_success "All services stopped" exit 0 } # Function to check if command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Function to check prerequisites check_prerequisites() { print_info "Checking prerequisites..." local missing_deps=() # Check for required commands if ! command_exists "uv"; then missing_deps+=("uv (Python package manager)") fi if ! command_exists "node"; then missing_deps+=("node (Node.js)") fi if ! command_exists "pnpm"; then missing_deps+=("pnpm (Node package manager)") fi if ! command_exists "curl"; then missing_deps+=("curl") fi if ! command_exists "lsof"; then print_warning "lsof not found, port checking will be limited" fi if [ ${#missing_deps[@]} -gt 0 ]; then print_error "Missing required dependencies:" for dep in "${missing_deps[@]}"; do echo " - $dep" done echo "" echo "Please install missing dependencies and try again." exit 1 fi print_success "All prerequisites satisfied" } # Function to check and free port check_and_free_port() { local port=$1 local name=$2 if ! command_exists "lsof"; then return 0 fi # Check if port is in use if lsof -i :$port >/dev/null 2>&1; then print_warning "Port $port ($name) is in use" # Get PIDs using the port local pids=$(lsof -ti :$port) # Ask user for confirmation read -p "Kill processes on port $port? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then for pid in $pids; do print_info "Killing process $pid on port $port..." kill -9 $pid 2>/dev/null done sleep 1 # Verify if port is still in use if lsof -i :$port >/dev/null 2>&1; then print_error "Failed to free port $port. Please free it manually." exit 1 else print_success "Port $port freed successfully" fi else print_error "Cannot start service on port $port. Exiting." exit 1 fi fi } # Function to check if directory exists check_directory() { local dir=$1 local name=$2 if [ ! -d "$dir" ]; then print_error "$name directory not found: $dir" exit 1 fi } # Function to start backend start_backend() { print_info "Starting Backend on port $BACKEND_PORT..." cd "$BACKEND_DIR" || exit 1 # Check if .env file exists if [ ! -f ".env" ]; then print_warning "Backend .env file not found. Using default configuration." fi # Set PYTHONPATH export PYTHONPATH=$PYTHONPATH:$(pwd) # Start backend with logging uv run uvicorn src.main:app --reload --port $BACKEND_PORT > "../$BACKEND_LOG" 2>&1 & BACKEND_PID=$! cd .. print_info "Backend PID: $BACKEND_PID (logs: $BACKEND_LOG)" } # Function to wait for backend to be ready wait_for_backend() { print_info "Waiting for backend to be ready..." local max_retries=30 local retry_count=0 local health_url="http://localhost:$BACKEND_PORT/health" while [ $retry_count -lt $max_retries ]; do if curl -s "$health_url" > /dev/null 2>&1; then print_success "Backend is ready!" return 0 fi # Check if backend process is still running if ! kill -0 $BACKEND_PID 2>/dev/null; then print_error "Backend process died. Check logs: $BACKEND_LOG" tail -n 20 "$BACKEND_LOG" return 1 fi retry_count=$((retry_count + 1)) # Show progress printf "\r Attempt %d/%d..." $retry_count $max_retries sleep 1 done echo "" print_error "Backend failed to start after $max_retries seconds" print_info "Last 20 lines of backend log:" tail -n 20 "$BACKEND_LOG" return 1 } # Function to start frontend start_frontend() { print_info "Starting Frontend on port $FRONTEND_PORT..." cd "$FRONTEND_DIR" || exit 1 # Check if node_modules exists if [ ! -d "node_modules" ]; then print_warning "node_modules not found. Running pnpm install..." pnpm install fi # Start frontend with logging pnpm dev > "../$FRONTEND_LOG" 2>&1 & FRONTEND_PID=$! cd .. print_info "Frontend PID: $FRONTEND_PID (logs: $FRONTEND_LOG)" } # Function to wait for frontend to be ready wait_for_frontend() { print_info "Waiting for frontend to be ready..." local max_retries=30 local retry_count=0 while [ $retry_count -lt $max_retries ]; do if curl -s "http://localhost:$FRONTEND_PORT" > /dev/null 2>&1; then print_success "Frontend is ready!" return 0 fi # Check if frontend process is still running if ! kill -0 $FRONTEND_PID 2>/dev/null; then print_error "Frontend process died. Check logs: $FRONTEND_LOG" tail -n 20 "$FRONTEND_LOG" return 1 fi retry_count=$((retry_count + 1)) # Show progress printf "\r Attempt %d/%d..." $retry_count $max_retries sleep 1 done echo "" print_error "Frontend failed to start after $max_retries seconds" print_info "Last 20 lines of frontend log:" tail -n 20 "$FRONTEND_LOG" return 1 } # Function to display service information display_info() { echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" print_success "All services started successfully!" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo " 📡 Backend API: http://localhost:$BACKEND_PORT" echo " 📚 API Docs: http://localhost:$BACKEND_PORT/docs" echo " 📖 ReDoc: http://localhost:$BACKEND_PORT/redoc" echo " 🏥 Health Check: http://localhost:$BACKEND_PORT/health" echo " 📊 Metrics: http://localhost:$BACKEND_PORT/metrics" echo "" echo " 🎨 Frontend: http://localhost:$FRONTEND_PORT" echo "" echo " 📝 Backend Logs: $BACKEND_LOG" echo " 📝 Frontend Logs: $FRONTEND_LOG" echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" print_info "Press Ctrl+C to stop all services" echo "" } # Main execution main() { echo "" echo "╔═══════════════════════════════════════════════════════╗" echo "║ Pixel Development Environment Startup ║" echo "╚═══════════════════════════════════════════════════════╝" echo "" # Trap SIGINT (Ctrl+C) and call cleanup trap cleanup SIGINT SIGTERM # Check prerequisites check_prerequisites # Check directories check_directory "$BACKEND_DIR" "Backend" check_directory "$FRONTEND_DIR" "Frontend" # Check and free ports check_and_free_port $BACKEND_PORT "Backend" check_and_free_port $FRONTEND_PORT "Frontend" # Start backend start_backend # Wait for backend to be ready if ! wait_for_backend; then cleanup exit 1 fi # Start frontend start_frontend # Wait for frontend to be ready if ! wait_for_frontend; then cleanup exit 1 fi # Display service information display_info # Wait for processes wait } # Run main function main