#!/bin/bash # ROA2WEB Ultrathin Monolith - Unified Starter Script # Starts all services for the unified application: # - SSH Tunnel (if needed for environment) # - Unified Backend (8000) - includes Reports, Data Entry, and Telegram # - Unified Frontend (3000) # # Usage: # ./start.sh prod Start production environment # ./start.sh test Start test environment # ./start.sh prod stop Stop production services # ./start.sh test stop Stop test services set -e # Exit on any error # Get the directory where this script is located SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Load NVM if available (required for npm) export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # No Color # ============================================================================ # Configuration based on environment # ============================================================================ ENV_NAME="" ENV_FILE="" LOG_DIR="" BACKEND_LOG="" FRONTEND_LOG="" NEEDS_SSH_TUNNEL=false BACKEND_RESTART_PID="" configure_environment() { case "$1" in prod|production) ENV_NAME="PROD" ENV_FILE=".env.prod" LOG_DIR="$SCRIPT_DIR/logs" BACKEND_LOG="$LOG_DIR/backend-stderr.log" FRONTEND_LOG="$LOG_DIR/frontend.log" NEEDS_SSH_TUNNEL=true ;; test) ENV_NAME="TEST" ENV_FILE=".env.test" LOG_DIR="/tmp" BACKEND_LOG="/tmp/unified_backend_test.log" FRONTEND_LOG="/tmp/unified_frontend_test.log" NEEDS_SSH_TUNNEL=false # Direct connection to 10.0.20.121 ;; *) echo -e "${RED}Error: Unknown environment '$1'${NC}" echo "" show_usage exit 1 ;; esac } show_usage() { echo -e "${BLUE}ROA2WEB Unified Starter${NC}" echo "" echo "Usage: $0 [action]" echo "" echo "Environments:" echo " prod, production Production environment (SSH tunnel to Oracle)" echo " test Test environment (direct connection to 10.0.20.121)" echo "" echo "Actions:" echo " start Start all services (default)" echo " stop Stop all services" echo "" echo "Examples:" echo " $0 prod Start production" echo " $0 test Start test" echo " $0 prod stop Stop production" echo " $0 test stop Stop test" echo "" } # ============================================================================ # Helper functions # ============================================================================ print_message() { echo -e "${CYAN}[UNIFIED-${ENV_NAME}]${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" } check_port() { local port=$1 if lsof -Pi :$port -sTCP:LISTEN -t >/dev/null 2>&1; then return 0 else return 1 fi } # ============================================================================ # Stop all services # ============================================================================ cleanup() { print_message "Stopping all services..." # Stop run-with-restart.sh wrapper FIRST by saved PID (prevents auto-restart race) if [ -n "$BACKEND_RESTART_PID" ] && kill -0 "$BACKEND_RESTART_PID" 2>/dev/null; then print_message "Stopping backend restart wrapper (PID $BACKEND_RESTART_PID)..." kill "$BACKEND_RESTART_PID" 2>/dev/null || true sleep 2 fi # Kill ALL uvicorn instances unconditionally (regardless of port status) # This handles cases where uvicorn is still starting up or has crashed print_message "Stopping Unified Backend..." pkill -f "uvicorn main:app" 2>/dev/null || true pkill -f "run-with-restart.sh" 2>/dev/null || true sleep 2 pkill -9 -f "uvicorn main:app" 2>/dev/null || true pkill -9 -f "run-with-restart.sh" 2>/dev/null || true lsof -ti:8000 | xargs kill -KILL 2>/dev/null || true # Stop Unified Frontend (3000) if check_port 3000; then print_message "Stopping Unified Frontend..." lsof -ti:3000 | xargs kill -TERM 2>/dev/null || true sleep 1 lsof -ti:3000 | xargs kill -KILL 2>/dev/null || true fi # Stop SSH tunnel (if script exists) if [ -f "$SCRIPT_DIR/ssh-tunnel.sh" ]; then print_message "Stopping SSH Tunnel..." "$SCRIPT_DIR/ssh-tunnel.sh" stop 2>/dev/null || true fi print_success "All services stopped." exit 0 } # ============================================================================ # Start all services # ============================================================================ start_services() { print_message "Starting ROA2WEB Ultrathin Monolith (${ENV_NAME} Environment)..." echo # Step 1: SSH Tunnel print_message "1. Checking SSH Tunnel..." if [ -f "$SCRIPT_DIR/ssh-tunnel.sh" ]; then if [ "$NEEDS_SSH_TUNNEL" = true ]; then if "$SCRIPT_DIR/ssh-tunnel.sh" start; then print_success "SSH Tunnel started" else print_warning "SSH tunnel may already be running or failed to start" fi sleep 2 else # For test - run anyway (will skip servers without ssh_host) "$SCRIPT_DIR/ssh-tunnel.sh" start 2>/dev/null || true print_success "${ENV_NAME} uses direct connection - no tunnel needed" sleep 1 fi else print_warning "SSH tunnel script not found - skipping" fi # Step 1.5: Check poppler-utils (required for PDF OCR) if ! command -v pdftoppm &> /dev/null; then print_warning "poppler-utils not found - required for PDF OCR processing" print_message "Installing poppler-utils..." if sudo apt-get update -qq && sudo apt-get install -y -qq poppler-utils; then print_success "poppler-utils installed" else print_warning "Could not install poppler-utils - PDF OCR may not work" fi else print_success "poppler-utils found ($(pdftoppm -v 2>&1 | head -1))" fi # Step 2: Start Unified Backend (8000) print_message "2. Starting Unified Backend on port 8000..." # Create and clear log files mkdir -p "$LOG_DIR" > "$BACKEND_LOG" > "$FRONTEND_LOG" if check_port 8000; then print_warning "Port 8000 already in use - Unified Backend may be running" else cd "$SCRIPT_DIR/backend/" # Create venv if doesn't exist if [ ! -d "venv" ]; then print_message "Creating Python virtual environment..." python3 -m venv venv fi # Activate venv source venv/bin/activate # Install dependencies if needed if ! python -c "import fastapi, uvicorn" 2>/dev/null; then print_message "Installing dependencies (this may take 3-5 minutes for ML packages)..." pip install -r requirements.txt fi # Check for environment configuration if [ ! -f "$ENV_FILE" ]; then print_error "$ENV_FILE not found!" print_warning "Please create $ENV_FILE from .env.example" exit 1 fi # Copy environment to active .env print_message "Using ${ENV_NAME} environment ($ENV_FILE)..." cp "$ENV_FILE" .env # Load environment set -a source .env set +a # Start backend with auto-restart on crash (OOM protection) print_message "Starting unified backend with auto-restart..." nohup ./run-with-restart.sh 8000 "$BACKEND_LOG" > /dev/null 2>&1 & BACKEND_RESTART_PID=$! cd "$SCRIPT_DIR" # Wait for backend to start print_message "Waiting for Unified Backend to initialize (Oracle + Cache + DBs + Telegram)..." MAX_WAIT=45 ELAPSED=0 while [ $ELAPSED -lt $MAX_WAIT ]; do if check_port 8000; then print_success "Unified Backend started on http://localhost:8000" break fi sleep 1 ELAPSED=$((ELAPSED + 1)) if [ $((ELAPSED % 5)) -eq 0 ]; then print_message "Still initializing... ($ELAPSED/${MAX_WAIT}s)" fi done if ! check_port 8000; then print_error "Unified Backend failed to start - check $BACKEND_LOG" tail -n 30 "$BACKEND_LOG" cleanup fi fi # Step 3: Start Unified Frontend (port 3000) print_message "3. Starting Unified Frontend (port 3000)..." if check_port 3000; then print_warning "Port 3000 already in use - Unified Frontend may be running" else cd "$SCRIPT_DIR" # Check if node_modules exists if [ ! -d "node_modules" ] || [ ! -f "node_modules/.bin/vite" ]; then print_message "Installing Unified Frontend dependencies..." npm install fi # Start frontend with NVM environment print_message "Starting Vite development server..." nohup bash -c 'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; npm run dev' > "$FRONTEND_LOG" 2>&1 & FRONTEND_PID=$! # Wait for frontend to start print_message "Waiting for Vite to initialize..." MAX_WAIT=15 ELAPSED=0 while [ $ELAPSED -lt $MAX_WAIT ]; do if check_port 3000; then print_success "Unified Frontend started on http://localhost:3000" break fi sleep 2 ELAPSED=$((ELAPSED + 2)) done if ! check_port 3000; then print_error "Unified Frontend failed to start - check $FRONTEND_LOG" cat "$FRONTEND_LOG" cleanup fi fi # Summary echo print_success "🚀 ROA2WEB Ultrathin Monolith (${ENV_NAME}) is now running!" echo echo -e "${BLUE}Services:${NC}" if [ "$NEEDS_SSH_TUNNEL" = true ]; then echo " • SSH Tunnel: Active (Oracle DB connection)" else echo " • Oracle Connection: Direct (no SSH tunnel needed)" fi echo " • Unified Backend: http://localhost:8000" echo " • ├── Reports API: http://localhost:8000/api/reports/*" echo " • ├── Data Entry: http://localhost:8000/api/data-entry/*" echo " • ├── Telegram: http://localhost:8000/api/telegram/*" echo " • └── Bot: Running as background task" echo " • Unified Frontend: http://localhost:3000" echo echo -e "${BLUE}API Documentation:${NC}" echo " • Unified API Docs: http://localhost:8000/docs" echo " • Health Check: http://localhost:8000/health" echo echo -e "${BLUE}Log Files:${NC}" echo " • Unified Backend: $BACKEND_LOG" echo " • Unified Frontend: $FRONTEND_LOG" echo echo -e "${YELLOW}Press Ctrl+C to stop all services${NC}" echo # Keep script running wait } # ============================================================================ # Main # ============================================================================ # Check arguments if [ $# -lt 1 ]; then show_usage exit 1 fi # Configure environment configure_environment "$1" # Set up signal handlers trap cleanup SIGINT SIGTERM # Execute action ACTION="${2:-start}" case "$ACTION" in start) start_services ;; stop) cleanup ;; *) print_error "Unknown action: $ACTION" show_usage exit 1 ;; esac