| |
| """Orchestration script to start both Cachy FastAPI backend and Flutter web frontend. |
| |
| Starts the FastAPI backend using uvicorn from the virtual environment and launches |
| the Flutter web frontend on Chrome. |
| """ |
|
|
| import shutil |
| import subprocess |
| import sys |
| import time |
| from pathlib import Path |
| from typing import List, Optional |
|
|
|
|
| def find_executable(name: str) -> Optional[str]: |
| """Find the path to an executable in PATH. |
| |
| Args: |
| name: The name of the executable to search for. |
| |
| Returns: |
| The path string if found, otherwise None. |
| """ |
| return shutil.which(name) |
|
|
|
|
| def run_app() -> int: |
| """Start the Cachy backend and Flutter frontend subprocesses. |
| |
| Returns: |
| Exit code (0 for clean shutdown, non-zero for error). |
| """ |
| root_dir: Path = Path(__file__).resolve().parent |
| backend_dir: Path = root_dir / "backend" |
| frontend_dir: Path = root_dir / "app" |
|
|
| venv_uvicorn: Path = backend_dir / ".venv" / "bin" / "uvicorn" |
| uvicorn_bin = str(venv_uvicorn) |
| if not venv_uvicorn.exists(): |
| fallback = find_executable("uvicorn") |
| if fallback is None: |
| print(f"Error: uvicorn not found in virtualenv or PATH.") |
| return 1 |
| uvicorn_bin = fallback |
|
|
| flutter_bin: Optional[str] = find_executable("flutter") |
| if flutter_bin is None: |
| print("Error: 'flutter' executable not found in PATH.") |
| return 1 |
|
|
| backend_cmd: List[str] = [ |
| uvicorn_bin, |
| "app.main:app", |
| "--reload", |
| |
| |
| "--host", |
| "0.0.0.0", |
| "--port", |
| "8000", |
| ] |
|
|
| frontend_cmd: List[str] = [ |
| flutter_bin, |
| "run", |
| "-d", |
| "chrome", |
| "--dart-define=CACHY_API_BASE=http://localhost:8000", |
| ] |
|
|
| print("Starting Cachy Backend (port 8000)...") |
| backend_proc: Optional[subprocess.Popen] = None |
| frontend_proc: Optional[subprocess.Popen] = None |
|
|
| try: |
| backend_proc = subprocess.Popen(backend_cmd, cwd=backend_dir) |
| except Exception as e: |
| print(f"Failed to start backend subprocess: {e}") |
| return 1 |
|
|
| time.sleep(2) |
|
|
| print("Starting Cachy Flutter Frontend on Chrome...") |
| try: |
| frontend_proc = subprocess.Popen(frontend_cmd, cwd=frontend_dir) |
| except Exception as e: |
| print(f"Failed to start frontend subprocess: {e}") |
| if backend_proc.poll() is None: |
| backend_proc.terminate() |
| return 1 |
|
|
| try: |
| frontend_proc.wait() |
| except KeyboardInterrupt: |
| print("\nReceived keyboard interrupt. Shutting down servers...") |
| except Exception as e: |
| print(f"Unexpected error during execution: {e}") |
| finally: |
| if frontend_proc and frontend_proc.poll() is None: |
| frontend_proc.terminate() |
| if backend_proc and backend_proc.poll() is None: |
| backend_proc.terminate() |
| try: |
| backend_proc.wait(timeout=5) |
| except subprocess.TimeoutExpired as e: |
| print(f"Backend did not terminate gracefully, killing process: {e}") |
| backend_proc.kill() |
|
|
| print("Cachy shutdown complete.") |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(run_app()) |
|
|