diff --git a/start_servers.py b/start_servers.py index 23da3087..aaf0e4ba 100755 --- a/start_servers.py +++ b/start_servers.py @@ -10,7 +10,6 @@ import sys import subprocess import signal import time -import json from pathlib import Path @@ -106,32 +105,36 @@ class StandaloneServerManager: subprocess.run( ["npm", "ci", "--silent"], cwd=str(frontend_dir), - check=True + check=True, + capture_output=True ) # Build if needed - if not (frontend_dir / ".next").exists(): + if not (frontend_dir / ".next" / "standalone").exists(): self.log("Building Next.js application...") subprocess.run( ["npm", "run", "build"], cwd=str(frontend_dir), - check=True + check=True, + capture_output=True ) def start_backend(self): - """Start FastAPI backend server""" + """Start FastAPI backend server using uvicorn directly from project root""" self.log(f"Starting backend on port {self.backend_port}...") backend_log = self.logs_dir / "backend.log" - backend_dir = self.root_dir / "backend" + env = os.environ.copy() env['PATH'] = f"{self.python_exe.parent}:{env['PATH']}" + # Use uvicorn CLI from venv to run the app with open(backend_log, 'w') as log_f: self.backend_process = subprocess.Popen( [ str(self.python_exe), - "-m", "uvicorn", + "-m", + "uvicorn", "main:app", "--host", "0.0.0.0", "--port", str(self.backend_port), @@ -140,7 +143,8 @@ class StandaloneServerManager: cwd=str(backend_dir), stdout=log_f, stderr=subprocess.STDOUT, - env=env + env=env, + preexec_fn=os.setsid # Create new process group to avoid signal issues ) self.log(f"✓ Backend started (PID: {self.backend_process.pid})") @@ -153,6 +157,7 @@ class StandaloneServerManager: env = os.environ.copy() env['PORT'] = str(self.frontend_port) + env['NODE_ENV'] = 'production' with open(frontend_log, 'w') as log_f: self.frontend_process = subprocess.Popen( @@ -160,7 +165,8 @@ class StandaloneServerManager: cwd=str(frontend_dir), stdout=log_f, stderr=subprocess.STDOUT, - env=env + env=env, + preexec_fn=os.setsid # Create new process group ) self.log(f"✓ Frontend started (PID: {self.frontend_process.pid})") @@ -205,8 +211,7 @@ class StandaloneServerManager: print(f" Frontend: tail -f {self.logs_dir / 'frontend.log'}") print(f" All logs: tail -f {self.logs_dir}/*.log") print("\nTo stop servers:") - print(f" kill {self.backend_process.pid} {self.frontend_process.pid}") - print("\nPress Ctrl+C to stop both servers") + print(f" Press Ctrl+C in this terminal") print("="*60 + "\n") def run(self): @@ -225,14 +230,16 @@ class StandaloneServerManager: self.print_summary() # Monitor processes - self.log("✓ Servers are running!") + self.log("✓ Servers are running! Press Ctrl+C to stop.") while True: # Check if processes are alive if self.backend_process.poll() is not None: self.log(f"⚠ Backend process exited with code {self.backend_process.returncode}") + self.log(" Check logs/backend.log for details") if self.frontend_process.poll() is not None: self.log(f"⚠ Frontend process exited with code {self.frontend_process.returncode}") + self.log(" Check logs/frontend.log for details") time.sleep(5) @@ -244,19 +251,27 @@ class StandaloneServerManager: def cleanup(self): """Gracefully shutdown servers""" + import signal + if self.backend_process: try: - self.backend_process.terminate() + os.killpg(os.getpgid(self.backend_process.pid), signal.SIGTERM) self.backend_process.wait(timeout=5) - except subprocess.TimeoutExpired: - self.backend_process.kill() + except Exception: + try: + self.backend_process.kill() + except Exception: + pass if self.frontend_process: try: - self.frontend_process.terminate() + os.killpg(os.getpgid(self.frontend_process.pid), signal.SIGTERM) self.frontend_process.wait(timeout=5) - except subprocess.TimeoutExpired: - self.frontend_process.kill() + except Exception: + try: + self.frontend_process.kill() + except Exception: + pass self.log("✓ Servers stopped") sys.exit(0)