From e74f1ccf36cb08e1160bda3c20219fedd4a50c6b Mon Sep 17 00:00:00 2001 From: Antoine Date: Wed, 3 Dec 2025 07:58:27 -0500 Subject: [PATCH] feat: Add single-command dashboard launcher script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - python launch_dashboard.py starts both backend and frontend - Ctrl+C gracefully shuts down both servers - Color-coded terminal output for status 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- launch_dashboard.py | 108 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 launch_dashboard.py diff --git a/launch_dashboard.py b/launch_dashboard.py new file mode 100644 index 00000000..ba28e614 --- /dev/null +++ b/launch_dashboard.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +""" +Atomizer Dashboard Launcher +Starts both backend (FastAPI) and frontend (Vite) with a single command. + +Usage: + python launch_dashboard.py + +Access dashboard at: http://localhost:3003 +""" + +import subprocess +import sys +import time +import os +import signal +from pathlib import Path + +# Colors for terminal output +class Colors: + GREEN = '\033[92m' + BLUE = '\033[94m' + YELLOW = '\033[93m' + RED = '\033[91m' + END = '\033[0m' + BOLD = '\033[1m' + +def print_banner(): + print(f""" +{Colors.BLUE}{Colors.BOLD}╔═══════════════════════════════════════════╗ +║ ATOMIZER DASHBOARD LAUNCHER ║ +╚═══════════════════════════════════════════╝{Colors.END} +""") + +def main(): + print_banner() + + root_dir = Path(__file__).parent + backend_dir = root_dir / "atomizer-dashboard" / "backend" + frontend_dir = root_dir / "atomizer-dashboard" / "frontend" + + # Verify directories exist + if not backend_dir.exists(): + print(f"{Colors.RED}Error: Backend directory not found at {backend_dir}{Colors.END}") + sys.exit(1) + if not frontend_dir.exists(): + print(f"{Colors.RED}Error: Frontend directory not found at {frontend_dir}{Colors.END}") + sys.exit(1) + + processes = [] + + try: + # Start backend + print(f"{Colors.YELLOW}Starting backend server (FastAPI on port 8000)...{Colors.END}") + backend_proc = subprocess.Popen( + ["python", "-m", "uvicorn", "api.main:app", "--reload", "--port", "8000"], + cwd=str(backend_dir), + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0 + ) + processes.append(("Backend", backend_proc)) + time.sleep(2) # Give backend time to start + + # Start frontend + print(f"{Colors.YELLOW}Starting frontend server (Vite on port 3003)...{Colors.END}") + frontend_proc = subprocess.Popen( + ["npm", "run", "dev"], + cwd=str(frontend_dir), + shell=True, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0 + ) + processes.append(("Frontend", frontend_proc)) + time.sleep(3) # Give frontend time to start + + print(f""" +{Colors.GREEN}{Colors.BOLD}Dashboard is running!{Colors.END} + +{Colors.BLUE}Access at:{Colors.END} http://localhost:3003 + +{Colors.YELLOW}Press Ctrl+C to stop both servers{Colors.END} +""") + + # Wait for processes + while True: + for name, proc in processes: + if proc.poll() is not None: + print(f"{Colors.RED}{name} server stopped unexpectedly{Colors.END}") + time.sleep(1) + + except KeyboardInterrupt: + print(f"\n{Colors.YELLOW}Shutting down servers...{Colors.END}") + finally: + # Clean up processes + for name, proc in processes: + try: + if sys.platform == "win32": + proc.terminate() + else: + os.killpg(os.getpgid(proc.pid), signal.SIGTERM) + proc.wait(timeout=5) + print(f"{Colors.GREEN}{name} stopped{Colors.END}") + except Exception as e: + print(f"{Colors.RED}Error stopping {name}: {e}{Colors.END}") + proc.kill() + + print(f"{Colors.GREEN}Dashboard shutdown complete{Colors.END}") + +if __name__ == "__main__": + main()