#!/usr/bin/env python3 """ Service startup script for AMO CRM service with FastStream workers. This script starts Redis, FastAPI server, and FastStream workers in the correct order for development. """ import subprocess import sys import time import logging import signal import os from typing import List logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Global list to track running processes processes: List[subprocess.Popen] = [] def cleanup_processes() -> None: """Clean up all running processes.""" logger.info("Cleaning up processes...") for process in processes: if process.poll() is None: # Process is still running logger.info(f"Terminating process {process.pid}") process.terminate() try: process.wait(timeout=5) except subprocess.TimeoutExpired: logger.warning(f"Force killing process {process.pid}") process.kill() def signal_handler(signum, frame) -> None: """Handle interrupt signals.""" logger.info("Received interrupt signal") cleanup_processes() sys.exit(0) def start_redis() -> bool: """ Start Redis server. Returns: True if Redis is running, False otherwise """ try: # Check if Redis is already running result = subprocess.run( ["redis-cli", "ping"], capture_output=True, text=True, timeout=5 ) if result.returncode == 0: logger.info("Redis is already running") return True except (subprocess.TimeoutExpired, FileNotFoundError): pass logger.info("Starting Redis server...") try: process = subprocess.Popen( ["redis-server", "--daemonize", "yes"], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # Wait a moment for Redis to start time.sleep(2) # Check if Redis is now running result = subprocess.run( ["redis-cli", "ping"], capture_output=True, text=True, timeout=5 ) if result.returncode == 0: logger.info("Redis started successfully") return True else: logger.error("Failed to start Redis") return False except FileNotFoundError: logger.error("Redis not found. Please install Redis first.") logger.info("On Ubuntu/Debian: sudo apt install redis-server") logger.info("On macOS: brew install redis") logger.info("On Windows: Download from https://redis.io/download") return False def start_fastapi() -> subprocess.Popen: """ Start FastAPI server. Returns: Process object for the FastAPI server """ logger.info("Starting FastAPI server...") process = subprocess.Popen([ "uv", "run", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--reload" ]) processes.append(process) return process def start_faststream_worker() -> subprocess.Popen: """ Start FastStream worker. Returns: Process object for the FastStream worker """ logger.info("Starting FastStream worker...") process = subprocess.Popen([ "uv", "run", "faststream", "run", "workers.broker:app", "--reload" ]) processes.append(process) return process def main() -> None: """Main function to start all services.""" # Set up signal handlers signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) logger.info("Starting AMO CRM services...") # Start Redis if not start_redis(): logger.error("Failed to start Redis. Exiting.") sys.exit(1) # Start FastAPI server fastapi_process = start_fastapi() logger.info(f"FastAPI server started (PID: {fastapi_process.pid})") # Wait a moment for FastAPI to start time.sleep(3) # Start FastStream worker worker_process = start_faststream_worker() logger.info(f"FastStream worker started (PID: {worker_process.pid})") logger.info("All services started successfully!") logger.info("FastAPI server: http://localhost:8000") logger.info("API documentation: http://localhost:8000/docs") logger.info("Press Ctrl+C to stop all services") try: # Wait for processes to complete while True: # Check if any process has died for process in processes: if process.poll() is not None: logger.error(f"Process {process.pid} has died") cleanup_processes() sys.exit(1) time.sleep(1) except KeyboardInterrupt: logger.info("Received interrupt, shutting down...") cleanup_processes() if __name__ == "__main__": main()