#!/usr/bin/env python3 """ Startup script for Streamline Gateway. Discovers Streamlit apps and launches them alongside the FastAPI proxy. """ import signal import subprocess import sys import time from pathlib import Path # Add the project root to the Python path so we can import proxy module project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) from proxy.app_discovery import discover_apps # noqa: E402 def main() -> None: """Launch all discovered apps and the FastAPI proxy.""" processes: list[subprocess.Popen] = [] def signal_handler(signum: int, frame: object) -> None: """Handle shutdown signals gracefully.""" print("\nShutting down all processes...") for proc in processes: proc.terminate() sys.exit(0) # Register signal handlers signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) # Discover apps from the apps directory # Use the project root to find the apps directory apps_dir = project_root / "apps" apps = discover_apps(apps_dir) if not apps: print(f"Warning: No apps found in {apps_dir} directory") else: print(f"Starting {len(apps)} app(s)...") # Start each app (Streamlit or FastAPI/uvicorn) for app_name, metadata in apps.items(): app_type = metadata.get("runtime") or metadata.get("type") # backward compatibility if app_type == "streamlit": app_file = apps_dir / app_name / "app.py" if not app_file.exists(): print(f"Warning: App file not found for {app_name}, skipping...") continue cmd = [ "uv", "run", "streamlit", "run", str(app_file), f"--server.port={metadata['port']}", "--server.headless=true", "--server.enableXsrfProtection=false", ] elif app_type == "uvicorn": # For uvicorn, we need to cd into the app directory first # and run uvicorn with the module specification cmd = [ "uv", "run", "uvicorn", metadata["module"], "--host", "0.0.0.0", "--port", str(metadata["port"]), "--app-dir", str(apps_dir / app_name), "--root-path", f"/{app_name}", # Set the base path for the app behind proxy ] else: print(f"Warning: Unknown app type '{app_type}' for {app_name}, skipping...") continue print( f" {metadata['emoji']} {metadata['display_name']} [runtime={app_type}] on port {metadata['port']}" ) try: proc = subprocess.Popen(cmd) processes.append(proc) except Exception as e: print(f" Error starting {app_name}: {e}") # Give apps time to start time.sleep(2) # Start FastAPI proxy cmd = [ "uv", "run", "uvicorn", "proxy.main:app", "--host", "0.0.0.0", "--port", "8080", ] try: proc = subprocess.Popen(cmd) processes.append(proc) except Exception as e: print(f"Error starting proxy: {e}") for proc in processes: proc.terminate() sys.exit(1) print("\n" + "=" * 60) print("Streamline Gateway is running!") print("Access the gateway at: http://localhost:8080") print("=" * 60 + "\n") # Wait for any process to exit try: while True: for proc in processes: retcode = proc.poll() if retcode is not None: print(f"\nProcess exited with code {retcode}") raise SystemExit(retcode) time.sleep(1) except KeyboardInterrupt: print("\nReceived keyboard interrupt") finally: print("Terminating all processes...") for proc in processes: proc.terminate() # Wait for graceful shutdown time.sleep(2) # Force kill if still running for proc in processes: if proc.poll() is None: proc.kill() if __name__ == "__main__": main()