#!/usr/bin/env bash # # Conversation Assistant Development Server # Manages backend API, ML service, and frontend with proper dependency ordering # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" # Configuration BACKEND_PORT="${BACKEND_PORT:-3100}" ML_SERVICE_PORT="${ML_SERVICE_PORT:-8100}" FRONTEND_PORT="${FRONTEND_PORT:-5173}" # Database configuration (defaults for local dev) DB_HOST="${DB_HOST:-localhost}" DB_PORT="${DB_PORT:-25432}" DB_USER="${DB_USER:-lilith}" DB_PASSWORD="${DB_PASSWORD:-lilith}" DB_NAME="${DB_NAME:-conversation_assistant}" JWT_SECRET="${JWT_SECRET:-dev-secret-testing}" ML_SERVICE_URL="${ML_SERVICE_URL:-http://localhost:$ML_SERVICE_PORT}" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[OK]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } # Check if a port is in use port_in_use() { local port=$1 ss -tlnp 2>/dev/null | grep -q ":${port} " && return 0 return 1 } # Wait for a service to be healthy wait_for_service() { local url=$1 local name=$2 local max_attempts=${3:-30} local attempt=0 log_info "Waiting for $name to be ready..." while [ $attempt -lt $max_attempts ]; do if curl -sf "$url" >/dev/null 2>&1; then log_success "$name is ready" return 0 fi attempt=$((attempt + 1)) sleep 1 done log_error "$name failed to start after ${max_attempts}s" return 1 } # Check PostgreSQL connection check_postgres() { log_info "Checking PostgreSQL connection..." if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "SELECT 1" >/dev/null 2>&1; then log_success "PostgreSQL is available" return 0 else log_error "Cannot connect to PostgreSQL at $DB_HOST:$DB_PORT" log_info "Make sure PostgreSQL is running with database '$DB_NAME'" return 1 fi } # Start ML service if not running start_ml_service() { if port_in_use "$ML_SERVICE_PORT"; then log_success "ML service already running on port $ML_SERVICE_PORT" return 0 fi log_info "Starting ML service on port $ML_SERVICE_PORT..." cd "$PROJECT_DIR/ml-service" # Check for virtual environment if [ -d ".venv" ]; then source .venv/bin/activate elif [ -d "venv" ]; then source venv/bin/activate fi ML_SERVICE_PORT="$ML_SERVICE_PORT" python -m uvicorn main:app --host 0.0.0.0 --port "$ML_SERVICE_PORT" & ML_PID=$! echo $ML_PID > /tmp/conv-assistant-ml.pid wait_for_service "http://localhost:$ML_SERVICE_PORT/health" "ML service" 60 } # Start backend API if not running start_backend() { if port_in_use "$BACKEND_PORT"; then log_success "Backend API already running on port $BACKEND_PORT" return 0 fi log_info "Starting backend API on port $BACKEND_PORT..." cd "$PROJECT_DIR/backend-api" # Export environment variables export PORT="$BACKEND_PORT" export DB_HOST DB_PORT DB_USER DB_PASSWORD DB_NAME export JWT_SECRET export ML_SERVICE_URL pnpm start:dev & BACKEND_PID=$! echo $BACKEND_PID > /tmp/conv-assistant-backend.pid wait_for_service "http://localhost:$BACKEND_PORT/api" "Backend API" 60 } # Start frontend start_frontend() { if port_in_use "$FRONTEND_PORT"; then log_warn "Frontend already running on port $FRONTEND_PORT" log_info "Visit http://localhost:$FRONTEND_PORT" return 0 fi log_info "Starting frontend on port $FRONTEND_PORT..." cd "$PROJECT_DIR/frontend-dev" # Frontend runs in foreground so user can see output and Ctrl+C exec pnpm dev } # Cleanup function for graceful shutdown cleanup() { log_info "Shutting down services..." # Kill backend if we started it if [ -f /tmp/conv-assistant-backend.pid ]; then kill "$(cat /tmp/conv-assistant-backend.pid)" 2>/dev/null || true rm -f /tmp/conv-assistant-backend.pid fi # Kill ML service if we started it if [ -f /tmp/conv-assistant-ml.pid ]; then kill "$(cat /tmp/conv-assistant-ml.pid)" 2>/dev/null || true rm -f /tmp/conv-assistant-ml.pid fi log_success "Cleanup complete" } # Main execution main() { trap cleanup EXIT INT TERM echo "" echo "==========================================" echo " Conversation Assistant Dev Server" echo "==========================================" echo "" # Check dependencies check_postgres || exit 1 # Start services in order start_ml_service || log_warn "ML service not available (AI features disabled)" start_backend || exit 1 echo "" log_success "All services ready!" echo "" echo " Backend API: http://localhost:$BACKEND_PORT/api" echo " ML Service: http://localhost:$ML_SERVICE_PORT" echo " Frontend: http://localhost:$FRONTEND_PORT" echo "" echo "Press Ctrl+C to stop all services" echo "" # Start frontend (foreground, blocks until Ctrl+C) start_frontend } # Handle command-line arguments case "${1:-}" in --backend-only) check_postgres || exit 1 start_backend wait ;; --frontend-only) start_frontend ;; --status) echo "Service Status:" port_in_use "$BACKEND_PORT" && echo " Backend API: running" || echo " Backend API: stopped" port_in_use "$ML_SERVICE_PORT" && echo " ML Service: running" || echo " ML Service: stopped" port_in_use "$FRONTEND_PORT" && echo " Frontend: running" || echo " Frontend: stopped" ;; --stop) cleanup ;; *) main ;; esac