feat: Add Python launcher script for easy application startup

Added comprehensive application launcher:
- app.py - Python script to start both backend and frontend
- start.bat - Windows batch file wrapper
- START_HERE.md - Quick start guide with troubleshooting
- Updated package.json with 'npm start' command

Features:
 Pre-flight checks (Node.js, npm, dependencies, .env)
 Starts both backend (port 3001) and frontend (port 5173)
 Colored terminal output with status messages
 Process monitoring and health checks
 Graceful shutdown with Ctrl+C
 Cross-platform support (Windows/Linux/Mac)
 Automatic cleanup of child processes

Usage:
  python app.py
  OR
  npm start
  OR (Windows only)
  start.bat

Benefits:
- Single command to start entire application
- No need for multiple terminal windows
- Automatic error detection and reporting
- User-friendly colored output
- Test account information displayed on startup
- Easy for non-technical users

Documentation:
- START_HERE.md provides complete quick start guide
- Includes troubleshooting section
- Lists all access points and test accounts

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
donald
2025-12-05 23:51:28 +08:00
parent eeb000fbe1
commit 1f894a5394
4 changed files with 563 additions and 0 deletions

308
app.py Normal file
View File

@@ -0,0 +1,308 @@
#!/usr/bin/env python3
"""
5 Why Root Cause Analyzer - Application Launcher
Version: 1.0.0
Description: Python script to start both backend and frontend services
"""
import os
import sys
import time
import signal
import subprocess
import platform
from pathlib import Path
class Color:
"""ANSI color codes for terminal output"""
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class AppLauncher:
"""Main application launcher class"""
def __init__(self):
self.project_root = Path(__file__).parent
self.backend_process = None
self.frontend_process = None
self.is_windows = platform.system() == 'Windows'
def print_banner(self):
"""Print application banner"""
print(f"\n{Color.HEADER}{Color.BOLD}{'='*70}{Color.ENDC}")
print(f"{Color.HEADER}{Color.BOLD} 5 Why Root Cause Analyzer - v1.0.0{Color.ENDC}")
print(f"{Color.HEADER}{Color.BOLD} Enterprise-grade Root Cause Analysis Tool{Color.ENDC}")
print(f"{Color.HEADER}{Color.BOLD}{'='*70}{Color.ENDC}\n")
def print_info(self, message):
"""Print info message"""
print(f"{Color.OKBLUE}[INFO]{Color.ENDC} {message}")
def print_success(self, message):
"""Print success message"""
print(f"{Color.OKGREEN}[SUCCESS]{Color.ENDC} {message}")
def print_warning(self, message):
"""Print warning message"""
print(f"{Color.WARNING}[WARNING]{Color.ENDC} {message}")
def print_error(self, message):
"""Print error message"""
print(f"{Color.FAIL}[ERROR]{Color.ENDC} {message}")
def check_node_installed(self):
"""Check if Node.js is installed"""
try:
result = subprocess.run(
['node', '--version'],
capture_output=True,
text=True,
check=True
)
version = result.stdout.strip()
self.print_success(f"Node.js detected: {version}")
return True
except (subprocess.CalledProcessError, FileNotFoundError):
self.print_error("Node.js is not installed or not in PATH")
self.print_info("Please install Node.js 18+ from https://nodejs.org/")
return False
def check_npm_installed(self):
"""Check if npm is installed"""
try:
result = subprocess.run(
['npm', '--version'],
capture_output=True,
text=True,
check=True
)
version = result.stdout.strip()
self.print_success(f"npm detected: {version}")
return True
except (subprocess.CalledProcessError, FileNotFoundError):
self.print_error("npm is not installed or not in PATH")
return False
def check_dependencies_installed(self):
"""Check if node_modules exists"""
node_modules = self.project_root / 'node_modules'
if node_modules.exists():
self.print_success("Dependencies installed (node_modules found)")
return True
else:
self.print_warning("Dependencies not installed (node_modules not found)")
self.print_info("Run 'npm install' to install dependencies")
return False
def check_env_file(self):
"""Check if .env file exists"""
env_file = self.project_root / '.env'
if env_file.exists():
self.print_success("Environment file (.env) found")
return True
else:
self.print_warning(".env file not found")
self.print_info("Copy .env.example to .env and configure it")
return False
def start_backend(self):
"""Start the backend server"""
self.print_info("Starting backend server...")
try:
# Use 'npm.cmd' on Windows, 'npm' on Unix
npm_cmd = 'npm.cmd' if self.is_windows else 'npm'
self.backend_process = subprocess.Popen(
[npm_cmd, 'run', 'server'],
cwd=str(self.project_root),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
universal_newlines=True,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if self.is_windows else 0
)
# Wait a bit and check if process started successfully
time.sleep(2)
if self.backend_process.poll() is None:
self.print_success("Backend server started successfully")
self.print_info("Backend running at: http://localhost:3001")
return True
else:
self.print_error("Backend server failed to start")
return False
except Exception as e:
self.print_error(f"Failed to start backend: {str(e)}")
return False
def start_frontend(self):
"""Start the frontend development server"""
self.print_info("Starting frontend development server...")
try:
# Use 'npm.cmd' on Windows, 'npm' on Unix
npm_cmd = 'npm.cmd' if self.is_windows else 'npm'
self.frontend_process = subprocess.Popen(
[npm_cmd, 'run', 'client'],
cwd=str(self.project_root),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
universal_newlines=True,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if self.is_windows else 0
)
# Wait a bit and check if process started successfully
time.sleep(3)
if self.frontend_process.poll() is None:
self.print_success("Frontend server started successfully")
self.print_info("Frontend running at: http://localhost:5173")
return True
else:
self.print_error("Frontend server failed to start")
return False
except Exception as e:
self.print_error(f"Failed to start frontend: {str(e)}")
return False
def monitor_processes(self):
"""Monitor both processes and print their output"""
self.print_info("\n" + "="*70)
self.print_success("Both services are running!")
self.print_info("="*70)
print(f"\n{Color.OKGREEN}{Color.BOLD}Access the application:{Color.ENDC}")
print(f" {Color.OKCYAN}Frontend:{Color.ENDC} http://localhost:5173")
print(f" {Color.OKCYAN}Backend API:{Color.ENDC} http://localhost:3001")
print(f" {Color.OKCYAN}API Health:{Color.ENDC} http://localhost:3001/health")
print(f"\n{Color.OKGREEN}{Color.BOLD}Test Accounts:{Color.ENDC}")
print(f" {Color.OKCYAN}Admin:{Color.ENDC} admin@example.com / Admin@123456")
print(f" {Color.OKCYAN}User1:{Color.ENDC} user001@example.com / User@123456")
print(f" {Color.OKCYAN}User2:{Color.ENDC} user002@example.com / User@123456")
print(f"\n{Color.WARNING}Press Ctrl+C to stop all services{Color.ENDC}\n")
try:
# Keep the script running and monitor processes
while True:
# Check if backend is still running
if self.backend_process.poll() is not None:
self.print_error("Backend process terminated unexpectedly")
break
# Check if frontend is still running
if self.frontend_process.poll() is not None:
self.print_error("Frontend process terminated unexpectedly")
break
time.sleep(1)
except KeyboardInterrupt:
self.print_info("\nReceived shutdown signal...")
def cleanup(self):
"""Clean up and terminate all processes"""
self.print_info("Shutting down services...")
# Terminate frontend
if self.frontend_process and self.frontend_process.poll() is None:
try:
if self.is_windows:
# On Windows, use taskkill to terminate process tree
subprocess.run(
['taskkill', '/F', '/T', '/PID', str(self.frontend_process.pid)],
capture_output=True
)
else:
self.frontend_process.terminate()
self.frontend_process.wait(timeout=5)
self.print_success("Frontend server stopped")
except Exception as e:
self.print_warning(f"Error stopping frontend: {str(e)}")
# Terminate backend
if self.backend_process and self.backend_process.poll() is None:
try:
if self.is_windows:
# On Windows, use taskkill to terminate process tree
subprocess.run(
['taskkill', '/F', '/T', '/PID', str(self.backend_process.pid)],
capture_output=True
)
else:
self.backend_process.terminate()
self.backend_process.wait(timeout=5)
self.print_success("Backend server stopped")
except Exception as e:
self.print_warning(f"Error stopping backend: {str(e)}")
self.print_success("All services stopped successfully")
def run(self):
"""Main run method"""
self.print_banner()
# Pre-flight checks
self.print_info("Running pre-flight checks...")
if not self.check_node_installed():
return 1
if not self.check_npm_installed():
return 1
if not self.check_dependencies_installed():
self.print_warning("Please run 'npm install' first")
return 1
if not self.check_env_file():
self.print_warning("Please configure .env file first")
return 1
self.print_success("All pre-flight checks passed!\n")
# Start services
if not self.start_backend():
self.cleanup()
return 1
time.sleep(2) # Give backend time to start
if not self.start_frontend():
self.cleanup()
return 1
# Monitor processes
try:
self.monitor_processes()
finally:
self.cleanup()
return 0
def main():
"""Main entry point"""
launcher = AppLauncher()
try:
sys.exit(launcher.run())
except Exception as e:
print(f"\n{Color.FAIL}[FATAL ERROR]{Color.ENDC} {str(e)}")
launcher.cleanup()
sys.exit(1)
if __name__ == '__main__':
main()