#!/usr/bin/env python3 """ Production-ready server startup script with proper error handling and monitoring. [LEGACY] This script is kept for historical compatibility. Preferred entrypoint is: ./scripts/service_ctl.sh start """ import os import sys import signal import time import subprocess import logging import argparse from typing import Dict, List, Optional import multiprocessing import threading # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(), logging.FileHandler('/tmp/search_engine_startup.log', mode='a') ] ) logger = logging.getLogger(__name__) class ServerManager: """Manages frontend and API server processes.""" def __init__(self): self.processes: Dict[str, subprocess.Popen] = {} self.running = True def start_frontend_server(self) -> bool: """Start the frontend server.""" try: frontend_script = os.path.join(os.path.dirname(__file__), 'frontend_server.py') cmd = [sys.executable, frontend_script] env = os.environ.copy() env['PYTHONUNBUFFERED'] = '1' process = subprocess.Popen( cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, bufsize=1 ) self.processes['frontend'] = process logger.info(f"Frontend server started with PID: {process.pid}") # Start monitoring thread threading.Thread( target=self._monitor_output, args=('frontend', process), daemon=True ).start() return True except Exception as e: logger.error(f"Failed to start frontend server: {e}") return False def start_api_server(self, es_host: str = "http://localhost:9200") -> bool: """Start the API server.""" try: cmd = [ sys.executable, 'main.py', 'serve', '--es-host', es_host, '--host', '0.0.0.0', '--port', '6002' ] env = os.environ.copy() env['PYTHONUNBUFFERED'] = '1' env['ES_HOST'] = es_host process = subprocess.Popen( cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, bufsize=1 ) self.processes['api'] = process logger.info(f"API server started with PID: {process.pid}") # Start monitoring thread threading.Thread( target=self._monitor_output, args=('api', process), daemon=True ).start() return True except Exception as e: logger.error(f"Failed to start API server: {e}") return False def _monitor_output(self, name: str, process: subprocess.Popen): """Monitor process output and log appropriately.""" try: for line in iter(process.stdout.readline, ''): if line.strip() and self.running: # Filter out scanner noise for frontend server if name == 'frontend': noise_patterns = [ 'code 400', 'Bad request version', 'Bad request syntax', 'Bad HTTP/0.9 request type' ] if any(pattern in line for pattern in noise_patterns): continue logger.info(f"[{name}] {line.strip()}") except Exception as e: if self.running: logger.error(f"Error monitoring {name} output: {e}") def check_servers(self) -> bool: """Check if all servers are still running.""" all_running = True for name, process in self.processes.items(): if process.poll() is not None: logger.error(f"{name} server has stopped with exit code: {process.returncode}") all_running = False return all_running def stop_all(self): """Stop all servers gracefully.""" logger.info("Stopping all servers...") self.running = False for name, process in self.processes.items(): try: logger.info(f"Stopping {name} server (PID: {process.pid})...") # Try graceful shutdown first process.terminate() # Wait up to 10 seconds for graceful shutdown try: process.wait(timeout=10) logger.info(f"{name} server stopped gracefully") except subprocess.TimeoutExpired: # Force kill if graceful shutdown fails logger.warning(f"{name} server didn't stop gracefully, forcing...") process.kill() process.wait() logger.info(f"{name} server stopped forcefully") except Exception as e: logger.error(f"Error stopping {name} server: {e}") self.processes.clear() logger.info("All servers stopped") def signal_handler(signum, frame): """Handle shutdown signals.""" logger.info(f"Received signal {signum}, shutting down...") if 'manager' in globals(): manager.stop_all() sys.exit(0) def main(): """Main function to start all servers.""" global manager parser = argparse.ArgumentParser(description='Start saas-search servers (multi-tenant)') parser.add_argument('--es-host', default='http://localhost:9200', help='Elasticsearch host') parser.add_argument('--check-dependencies', action='store_true', help='Check dependencies before starting') args = parser.parse_args() logger.info("Starting saas-search servers (multi-tenant)...") logger.info(f"Elasticsearch: {args.es_host}") # Check dependencies if requested if args.check_dependencies: logger.info("Checking dependencies...") try: import slowapi import anyio logger.info("✓ All dependencies available") except ImportError as e: logger.error(f"✗ Missing dependency: {e}") logger.info("Please run: pip install -r requirements_server.txt") sys.exit(1) manager = ServerManager() # Set up signal handlers signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) try: # Start servers if not manager.start_api_server(args.es_host): logger.error("Failed to start API server") sys.exit(1) # Wait a moment before starting frontend server time.sleep(2) if not manager.start_frontend_server(): logger.error("Failed to start frontend server") manager.stop_all() sys.exit(1) logger.info("All servers started successfully!") logger.info("Frontend: http://localhost:6003") logger.info("API: http://localhost:6002") logger.info("API Docs: http://localhost:6002/docs") logger.info("Press Ctrl+C to stop all servers") # Monitor servers while manager.running: if not manager.check_servers(): logger.error("One or more servers have stopped unexpectedly") manager.stop_all() sys.exit(1) time.sleep(5) # Check every 5 seconds except KeyboardInterrupt: logger.info("Received interrupt signal") except Exception as e: logger.error(f"Unexpected error: {e}") finally: manager.stop_all() if __name__ == '__main__': main()