#!/Users/stwhite/CODE/chatterbox-ui/.venv/bin/python """ Startup script that launches both the backend and frontend servers concurrently. """ import os import sys import time import signal import subprocess import threading from pathlib import Path # Try to load environment variables, but don't fail if dotenv is not available try: from dotenv import load_dotenv load_dotenv() except ImportError: print("python-dotenv not installed, using system environment variables only") # Configuration BACKEND_PORT = int(os.getenv("BACKEND_PORT", "8000")) BACKEND_HOST = os.getenv("BACKEND_HOST", "0.0.0.0") # Frontend host/port (for dev server binding) FRONTEND_PORT = int(os.getenv("FRONTEND_PORT", "8001")) FRONTEND_HOST = os.getenv("FRONTEND_HOST", "0.0.0.0") # Export frontend host/port so backend CORS config can pick them up automatically os.environ["FRONTEND_HOST"] = FRONTEND_HOST os.environ["FRONTEND_PORT"] = str(FRONTEND_PORT) # Get project root directory PROJECT_ROOT = Path(__file__).parent.absolute() def run_backend(): """Run the backend FastAPI server""" os.chdir(PROJECT_ROOT / "backend") cmd = [ sys.executable, "-m", "uvicorn", "app.main:app", "--reload", f"--host={BACKEND_HOST}", f"--port={BACKEND_PORT}", ] print(f"\n{'='*50}") print(f"Starting Backend Server at http://{BACKEND_HOST}:{BACKEND_PORT}") print(f"API docs available at http://{BACKEND_HOST}:{BACKEND_PORT}/docs") print(f"{'='*50}\n") return subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, bufsize=1, ) def run_frontend(): """Run the frontend development server""" frontend_dir = PROJECT_ROOT / "frontend" os.chdir(frontend_dir) cmd = [sys.executable, "start_dev_server.py"] env = os.environ.copy() env["VITE_DEV_SERVER_HOST"] = FRONTEND_HOST env["VITE_DEV_SERVER_PORT"] = str(FRONTEND_PORT) print(f"\n{'='*50}") print(f"Starting Frontend Server at http://{FRONTEND_HOST}:{FRONTEND_PORT}") print(f"{'='*50}\n") return subprocess.Popen( cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, bufsize=1, ) def print_process_output(process, prefix): """Print process output with a prefix""" for line in iter(process.stdout.readline, ""): if not line: break print(f"{prefix} | {line}", end="") def main(): """Main function to start both servers""" print("\nšŸš€ Starting Chatterbox UI Development Environment") # Start the backend server backend_process = run_backend() # Give the backend a moment to start time.sleep(2) # Start the frontend server frontend_process = run_frontend() # Create threads to monitor and print output backend_monitor = threading.Thread( target=print_process_output, args=(backend_process, "BACKEND"), daemon=True ) frontend_monitor = threading.Thread( target=print_process_output, args=(frontend_process, "FRONTEND"), daemon=True ) backend_monitor.start() frontend_monitor.start() # Setup signal handling for graceful shutdown def signal_handler(sig, frame): print("\n\nšŸ›‘ Shutting down servers...") backend_process.terminate() frontend_process.terminate() # Threads are daemon, so they'll exit when the main thread exits print("āœ… Servers stopped successfully") sys.exit(0) signal.signal(signal.SIGINT, signal_handler) # Print access information print("\nšŸ“‹ Access Information:") print(f" • Frontend: http://{FRONTEND_HOST}:{FRONTEND_PORT}") print(f" • Backend API: http://{BACKEND_HOST}:{BACKEND_PORT}/api") print(f" • API Documentation: http://{BACKEND_HOST}:{BACKEND_PORT}/docs") print("\nāš ļø Press Ctrl+C to stop both servers\n") # Keep the main process running try: while True: time.sleep(1) except KeyboardInterrupt: signal_handler(None, None) if __name__ == "__main__": main()