#!/bin/bash # Tool_OCR - Production Server Startup Script # Usage: # ./start-prod.sh Start all services (backend + frontend) # ./start-prod.sh backend Start only backend # ./start-prod.sh frontend Start only frontend # ./start-prod.sh --stop Stop all services # ./start-prod.sh --status Show service status # ./start-prod.sh --help Show help # Note: We don't use 'set -e' here because stop commands may fail gracefully # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PID_DIR="$SCRIPT_DIR/.pid-prod" BACKEND_PID_FILE="$PID_DIR/backend.pid" FRONTEND_PID_FILE="$PID_DIR/frontend.pid" # Production defaults (different from development) BACKEND_HOST=${BACKEND_HOST:-0.0.0.0} BACKEND_PORT=${BACKEND_PORT:-8000} FRONTEND_HOST=${FRONTEND_HOST:-0.0.0.0} FRONTEND_PORT=${FRONTEND_PORT:-12010} # Production-specific settings UVICORN_WORKERS=${UVICORN_WORKERS:-4} # Create PID directory mkdir -p "$PID_DIR" # Functions print_header() { echo "" echo -e "${BLUE}================================${NC}" echo -e "${BLUE} Tool_OCR Production Server${NC}" echo -e "${BLUE}================================${NC}" echo "" } show_help() { echo "Tool_OCR - Production Server Manager" echo "" echo "Usage: ./start-prod.sh [command]" echo "" echo "Commands:" echo " (none) Start all services (backend + frontend)" echo " backend Start only backend service" echo " frontend Start only frontend service" echo " --stop Stop all running services" echo " --status Show status of services" echo " --help Show this help message" echo "" echo "Environment Variables:" echo " BACKEND_PORT Backend port (default: 8000)" echo " FRONTEND_PORT Frontend port (default: 12010)" echo " UVICORN_WORKERS Number of uvicorn workers (default: 4)" echo "" echo "Examples:" echo " ./start-prod.sh # Start everything" echo " ./start-prod.sh backend # Start only backend" echo " FRONTEND_PORT=3000 ./start-prod.sh # Custom port" echo " ./start-prod.sh --stop # Stop all services" echo "" } check_requirements() { local missing=0 # Check virtual environment if [ ! -d "$SCRIPT_DIR/venv" ]; then echo -e "${RED}Error: Python virtual environment not found${NC}" echo "Please run: ./setup_dev_env.sh" missing=1 fi # Check node_modules if [ ! -d "$SCRIPT_DIR/frontend/node_modules" ]; then echo -e "${RED}Error: Frontend dependencies not found${NC}" echo "Please run: ./setup_dev_env.sh" missing=1 fi # Check .env (production uses .env, not .env.local) if [ ! -f "$SCRIPT_DIR/.env" ]; then echo -e "${YELLOW}Warning: .env not found, using defaults${NC}" fi return $missing } load_env() { # Production loads .env only (not .env.local) # For production, you should use .env with production settings if [ -f "$SCRIPT_DIR/.env" ]; then set -a # shellcheck disable=SC1090 source "$SCRIPT_DIR/.env" set +a fi } is_running() { local pid_file=$1 if [ -f "$pid_file" ]; then local pid=$(cat "$pid_file") if ps -p "$pid" > /dev/null 2>&1; then return 0 else # PID file exists but process is not running, clean up rm -f "$pid_file" fi fi return 1 } get_pid() { local pid_file=$1 if [ -f "$pid_file" ]; then cat "$pid_file" fi } start_backend() { if is_running "$BACKEND_PID_FILE"; then echo -e "${YELLOW}Backend already running (PID: $(get_pid $BACKEND_PID_FILE))${NC}" return 0 fi echo -e "${GREEN}Starting backend server (production mode)...${NC}" # Activate virtual environment source "$SCRIPT_DIR/venv/bin/activate" # Load environment variables load_env # Start backend in background cd "$SCRIPT_DIR/backend" # Create necessary directories mkdir -p uploads/{temp,processed,images} mkdir -p storage/{markdown,json,exports,results} mkdir -p models/paddleocr mkdir -p logs # Start uvicorn in production mode (no --reload, with workers) nohup uvicorn app.main:app \ --host "$BACKEND_HOST" \ --port "$BACKEND_PORT" \ --workers "$UVICORN_WORKERS" \ --access-log \ --log-level info \ > "$PID_DIR/backend.log" 2>&1 & local pid=$! echo $pid > "$BACKEND_PID_FILE" cd "$SCRIPT_DIR" # Wait a moment and verify sleep 3 if is_running "$BACKEND_PID_FILE"; then echo -e "${GREEN}Backend started (PID: $pid, Workers: $UVICORN_WORKERS)${NC}" echo -e " API Docs: http://localhost:$BACKEND_PORT/docs" echo -e " Health: http://localhost:$BACKEND_PORT/health" else echo -e "${RED}Backend failed to start. Check $PID_DIR/backend.log${NC}" return 1 fi } start_frontend() { if is_running "$FRONTEND_PID_FILE"; then echo -e "${YELLOW}Frontend already running (PID: $(get_pid $FRONTEND_PID_FILE))${NC}" return 0 fi echo -e "${GREEN}Starting frontend server (production mode)...${NC}" # Load environment variables load_env # Load nvm export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" cd "$SCRIPT_DIR/frontend" # Build frontend if dist doesn't exist or is older than src if [ ! -d "dist" ] || [ "$(find src -newer dist -type f 2>/dev/null | head -1)" ]; then echo -e "${YELLOW}Building frontend...${NC}" npm run build fi # Start production preview server # Note: For real production, use nginx to serve dist/ folder nohup npm run preview -- --host "$FRONTEND_HOST" --port "$FRONTEND_PORT" > "$PID_DIR/frontend.log" 2>&1 & local pid=$! echo $pid > "$FRONTEND_PID_FILE" cd "$SCRIPT_DIR" # Wait a moment and verify sleep 3 if is_running "$FRONTEND_PID_FILE"; then echo -e "${GREEN}Frontend started (PID: $pid)${NC}" echo -e " URL: http://localhost:$FRONTEND_PORT" else echo -e "${RED}Frontend failed to start. Check $PID_DIR/frontend.log${NC}" return 1 fi } kill_process_tree() { local pid=$1 # Kill all child processes first pkill -TERM -P "$pid" 2>/dev/null || true # Then kill the parent kill -TERM "$pid" 2>/dev/null || true } force_kill_process_tree() { local pid=$1 # Force kill all child processes pkill -9 -P "$pid" 2>/dev/null || true # Force kill the parent kill -9 "$pid" 2>/dev/null || true } kill_by_port() { local port=$1 local pids=$(lsof -ti :$port 2>/dev/null) if [ -n "$pids" ]; then echo "$pids" | xargs kill -TERM 2>/dev/null || true sleep 1 # Force kill if still running pids=$(lsof -ti :$port 2>/dev/null) if [ -n "$pids" ]; then echo "$pids" | xargs kill -9 2>/dev/null || true fi fi } stop_service() { local name=$1 local pid_file=$2 local port=$3 if is_running "$pid_file"; then local pid=$(get_pid "$pid_file") echo -e "${YELLOW}Stopping $name (PID: $pid)...${NC}" # Kill the entire process tree kill_process_tree "$pid" # Wait up to 5 seconds local count=0 while [ $count -lt 5 ] && is_running "$pid_file"; do sleep 1 count=$((count + 1)) done # Force kill if still running if is_running "$pid_file"; then force_kill_process_tree "$pid" fi rm -f "$pid_file" fi # Also kill any orphaned processes by port (fallback) if [ -n "$port" ]; then local port_pids=$(lsof -ti :$port 2>/dev/null) if [ -n "$port_pids" ]; then echo -e "${YELLOW}Cleaning up orphaned processes on port $port...${NC}" kill_by_port "$port" fi fi echo -e "${GREEN}$name stopped${NC}" } stop_all() { echo -e "${YELLOW}Stopping all production services...${NC}" stop_service "Backend" "$BACKEND_PID_FILE" "$BACKEND_PORT" stop_service "Frontend" "$FRONTEND_PID_FILE" "$FRONTEND_PORT" echo -e "${GREEN}All services stopped${NC}" } show_status() { echo -e "${BLUE}Production Service Status:${NC}" echo "" if is_running "$BACKEND_PID_FILE"; then local pid=$(get_pid "$BACKEND_PID_FILE") echo -e " Backend: ${GREEN}Running${NC} (PID: $pid, Workers: $UVICORN_WORKERS)" echo -e " http://localhost:$BACKEND_PORT" else echo -e " Backend: ${RED}Stopped${NC}" fi if is_running "$FRONTEND_PID_FILE"; then local pid=$(get_pid "$FRONTEND_PID_FILE") echo -e " Frontend: ${GREEN}Running${NC} (PID: $pid)" echo -e " http://localhost:$FRONTEND_PORT" else echo -e " Frontend: ${RED}Stopped${NC}" fi echo "" } # Main case "${1:-all}" in --help|-h) show_help ;; --stop) stop_all ;; --status) show_status ;; backend) print_header check_requirements || exit 1 start_backend echo "" echo -e "${YELLOW}Logs: tail -f $PID_DIR/backend.log${NC}" ;; frontend) print_header check_requirements || exit 1 start_frontend echo "" echo -e "${YELLOW}Logs: tail -f $PID_DIR/frontend.log${NC}" ;; all|"") print_header check_requirements || exit 1 start_backend start_frontend echo "" echo -e "${GREEN}================================${NC}" echo -e "${GREEN}All production services started!${NC}" echo -e "${GREEN}================================${NC}" echo "" echo "Access the application:" echo -e " Frontend: ${BLUE}http://localhost:$FRONTEND_PORT${NC}" echo -e " API Docs: ${BLUE}http://localhost:$BACKEND_PORT/docs${NC}" echo "" echo -e "${YELLOW}Use ./start-prod.sh --stop to stop all services${NC}" echo -e "${YELLOW}Use ./start-prod.sh --status to check status${NC}" echo "" echo "Logs:" echo " Backend: tail -f $PID_DIR/backend.log" echo " Frontend: tail -f $PID_DIR/frontend.log" echo "" echo -e "${YELLOW}Note: For real production deployment, consider using:${NC}" echo " - nginx as reverse proxy" echo " - systemd for service management" echo " - Docker for containerization" ;; *) echo -e "${RED}Unknown command: $1${NC}" show_help exit 1 ;; esac