#!/usr/bin/env bash # --- CONFIGURATION (Default values, will be overridden by network_configinventory.env) --- BACKEND_PORT=8000 FRONTEND_PORT=3001 BACKEND_SSL_PORT=3002 FRONTEND_SSL_PORT=3003 SERVER_IP="localhost" # Load Configuration from file if it exists CONFIG_PATH="$(cd "$(dirname "$0")" && pwd)/inventory.env" if [ -f "$CONFIG_PATH" ]; then echo "⚙️ Loading network configuration from $CONFIG_PATH..." # Export variables from inventory.env file (ignoring comments and empty lines) export $(grep -v '^#' "$CONFIG_PATH" | xargs) fi echo "🚀 Starting TFM aInventory Stack with Dual Proxy..." # 1. COMPREHENSIVE process cleanup - ensure absolute clean state echo "Sweep: Comprehensive process cleanup..." # Kill all known hanging processes pkill -9 -f "uvicorn" 2>/dev/null || true pkill -9 -f "next-server" 2>/dev/null || true pkill -9 -f "local-ssl-proxy" 2>/dev/null || true pkill -9 -f "python.*backend" 2>/dev/null || true pkill -9 -f "python.*main:app" 2>/dev/null || true pkill -9 -f "npm run dev" 2>/dev/null || true pkill -9 -f "node.*next" 2>/dev/null || true # Kill any remaining Python processes on port 8000 (backend) fuser -k 8000/tcp 2>/dev/null || true fuser -k 3001/tcp 2>/dev/null || true fuser -k 3002/tcp 2>/dev/null || true fuser -k 3003/tcp 2>/dev/null || true # Extra wait to ensure processes are fully dead sleep 1 echo "✅ Cleanup complete - all processes terminated" # 2. Setup/Activate Virtual Environment if [ ! -f ".venv/bin/activate" ]; then echo "🌑 Creating virtual environment (.venv)..." rm -rf .venv apt install python3-venv python3 -m venv .venv || { echo "❌ Failed to create venv"; exit 1; } fi source .venv/bin/activate || { echo "❌ Failed to activate venv"; exit 1; } # 3. Check and Install Backend Dependencies echo "📦 Updating Python dependencies..." .venv/bin/pip install -r backend/requirements.txt # 4. Get Local IP and set environment variables LOCAL_IP=$(hostname -I | awk '{print $1}' || echo "localhost") export ALLOWED_ORIGINS="http://localhost:$FRONTEND_PORT,http://localhost:$BACKEND_PORT,https://localhost:$FRONTEND_SSL_PORT,https://localhost:$BACKEND_SSL_PORT,https://$LOCAL_IP:$FRONTEND_SSL_PORT,https://$LOCAL_IP:$BACKEND_SSL_PORT" # 4.0 Include EXTRA_ALLOWED_ORIGINS from inventory.env (VPN, Tailscale, etc.) if [ ! -z "$EXTRA_ALLOWED_ORIGINS" ]; then echo "🔌 Adding extra CORS origins from inventory.env..." IFS=',' read -ra EXTRA_ADDRS <<< "$EXTRA_ALLOWED_ORIGINS" for addr in "${EXTRA_ADDRS[@]}"; do TRIMMED=$(echo "$addr" | xargs) # Add both HTTP (for localhost dev) and HTTPS (for production) export ALLOWED_ORIGINS="$ALLOWED_ORIGINS,http://$TRIMMED:$FRONTEND_PORT,http://$TRIMMED:$BACKEND_PORT,https://$TRIMMED:$FRONTEND_SSL_PORT,https://$TRIMMED:$BACKEND_SSL_PORT" done fi export JWT_SECRET_KEY="${JWT_SECRET_KEY:-ephemeral-dev-key-$(date +%s)}" export DATA_DIR="$(cd "$(dirname "$0")" && pwd)/data" export LOGS_DIR="$(cd "$(dirname "$0")" && pwd)/logs" # First-run: initialize data directories and config templates echo "🔧 Initializing runtime data directories..." bash "$(cd "$(dirname "$0")" && pwd)/scripts/init_data.sh" # 4.1 Sync Network Config to Frontend for runtime discovery echo "🔌 Syncing network configuration to frontend..." cat < frontend/public/network.json { "SERVER_IP": "$SERVER_IP", "BACKEND_PORT": $BACKEND_PORT, "BACKEND_SSL_PORT": $BACKEND_SSL_PORT, "FRONTEND_PORT": $FRONTEND_PORT, "FRONTEND_SSL_PORT": $FRONTEND_SSL_PORT } EOF echo "🔥 Starting Backend on port $BACKEND_PORT..." echo " CORS origins: $ALLOWED_ORIGINS" .venv/bin/python -m uvicorn backend.main:app --host 0.0.0.0 --port $BACKEND_PORT --reload & # 5. Prepare Frontend Dev Origins from EXTRA_ALLOWED_ORIGINS # Convert subnet notation (10.0.0.0/24) to wildcard patterns (10.0.0.*) ALLOWED_DEV_ORIGINS="localhost,127.0.0.1,*.local" if [ ! -z "$EXTRA_ALLOWED_ORIGINS" ]; then IFS=',' read -ra EXTRA_ADDRS <<< "$EXTRA_ALLOWED_ORIGINS" for addr in "${EXTRA_ADDRS[@]}"; do TRIMMED=$(echo "$addr" | xargs) if [[ "$TRIMMED" == *"/"* ]]; then # Subnet notation: convert 100.78.182.0/24 -> 100.78.182.* SUBNET_PREFIX=$(echo "$TRIMMED" | cut -d'/' -f1) SUBNET_PATTERN="${SUBNET_PREFIX%.*}.*" ALLOWED_DEV_ORIGINS="$ALLOWED_DEV_ORIGINS,$SUBNET_PATTERN" else # Individual IP: add wildcard for nearby IPs (e.g., 192.168.1.100 -> 192.168.1.*) IP_PATTERN="${TRIMMED%.*}.*" ALLOWED_DEV_ORIGINS="$ALLOWED_DEV_ORIGINS,$IP_PATTERN" fi done fi export ALLOWED_DEV_ORIGINS # 5. Start Frontend (Next.js) echo "💻 Starting Frontend on port $FRONTEND_PORT..." echo " Dev origins: $ALLOWED_DEV_ORIGINS" # Check Node.js version NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1) if [ "$NODE_VERSION" -lt 20 ]; then echo "⚠️ WARNING: Node.js v20+ required, but found $(node -v)" echo " Please upgrade Node.js: https://nodejs.org/" fi cd frontend echo "📦 Installing frontend dependencies..." npm install ALLOWED_DEV_ORIGINS="$ALLOWED_DEV_ORIGINS" npm run dev -- -p $FRONTEND_PORT & cd .. # 6. Start Proxies (Crucial for Mobile/Tablet Camera & Sync) # Bind to SERVER_IP (not 0.0.0.0) so VPN/remote clients can reach the server PROXY_HOSTNAME=${SERVER_IP:-0.0.0.0} echo "🔐 Starting SSL proxies on $PROXY_HOSTNAME..." npx local-ssl-proxy --source $BACKEND_SSL_PORT --target $BACKEND_PORT --hostname $PROXY_HOSTNAME > /dev/null 2>&1 & npx local-ssl-proxy --source $FRONTEND_SSL_PORT --target $FRONTEND_PORT --hostname $PROXY_HOSTNAME > /dev/null 2>&1 & # 7. Signal handlers for clean shutdown cleanup() { echo "" echo "🛑 Shutting down services..." pkill -P $$ > /dev/null 2>&1 kill $(jobs -p) 2>/dev/null || true pkill -f "uvicorn" 2>/dev/null || true pkill -f "next-server" 2>/dev/null || true pkill -f "local-ssl-proxy" 2>/dev/null || true echo "✅ Cleanup complete" exit 0 } # Trap Ctrl-C (SIGINT) and other termination signals trap cleanup SIGINT SIGTERM EXIT # 8. Print Unified Access Banner # Colors GREEN='\033[0;32m' BOLD='\033[1m' YELLOW='\033[1;33m' NC='\033[0m' # No Color echo "" echo -e "${GREEN}=======================================================${NC}" echo -e "${GREEN}${BOLD} 🚀 TFM aInventory UNIFIED ACCESS${NC}" echo -e "${GREEN}=======================================================${NC}" echo "" echo -e " USE THIS URL ON BOTH DESKTOP & MOBILE:" echo -e " 👉 ${GREEN}${BOLD}https://$LOCAL_IP:$FRONTEND_SSL_PORT${NC}" echo -e " (Or ${GREEN}https://localhost:$FRONTEND_SSL_PORT${NC} on this Mac)" if [ ! -z "$EXTRA_ALLOWED_ORIGINS" ]; then echo -e " EXTERNAL/VPN ACCESS points:" IFS=',' read -ra ADDR <<< "$EXTRA_ALLOWED_ORIGINS" for exp in "${ADDR[@]}"; do # Trim spaces and print TRIMMED=$(echo $exp | xargs) echo -e " 👉 ${GREEN}${BOLD}https://$TRIMMED:$FRONTEND_SSL_PORT${NC}" done fi echo "" echo -e " ${YELLOW}${BOLD}NOTE:${NC} If you see a 'Not Private' warning," echo -e " Click 'Advanced' -> 'Proceed' to continue." echo -e "${GREEN}=======================================================${NC}" echo "Keep this window open while working. Press Ctrl-C to stop." echo -e "${GREEN}=======================================================${NC}" # Wait for background processes wait