176 lines
4.7 KiB
Python
176 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
CADDesigner React Web UI startup script.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
project_root = Path(__file__).parent
|
|
frontend_root = project_root / "frontend"
|
|
|
|
|
|
def resolve_browser_host(host: str) -> str:
|
|
"""Convert a bind address to a display address better suited for browser access."""
|
|
if host in {"0.0.0.0", "::"}:
|
|
return "localhost"
|
|
return host
|
|
|
|
|
|
def resolve_api_url(api_url: str | None) -> str:
|
|
"""Resolve the API address that the React UI should connect to."""
|
|
if api_url:
|
|
return api_url.rstrip("/")
|
|
|
|
env_api_url = os.getenv("CADDESIGNER_API_URL")
|
|
if env_api_url:
|
|
return env_api_url.rstrip("/")
|
|
|
|
return "http://127.0.0.1:8000"
|
|
|
|
|
|
def resolve_package_manager() -> tuple[str, str]:
|
|
"""Prefer pnpm, then fall back to npm."""
|
|
pnpm_cmd = shutil.which("pnpm")
|
|
if pnpm_cmd:
|
|
return pnpm_cmd, "pnpm"
|
|
|
|
npm_cmd = shutil.which("npm")
|
|
if npm_cmd:
|
|
return npm_cmd, "npm"
|
|
|
|
raise RuntimeError("Neither pnpm nor npm was found. Please install a Node.js package manager first.")
|
|
|
|
|
|
def ensure_frontend_ready(debug: bool = False) -> tuple[str, str]:
|
|
"""Ensure React frontend dependencies are ready and return package manager information."""
|
|
if not frontend_root.is_dir():
|
|
raise RuntimeError(f"React frontend directory does not exist: {frontend_root}")
|
|
|
|
package_manager_cmd, package_manager_name = resolve_package_manager()
|
|
|
|
if not (frontend_root / "node_modules").exists():
|
|
print(
|
|
f"React frontend dependencies are not installed yet; running {package_manager_name} install ..."
|
|
)
|
|
install_result = subprocess.run(
|
|
[package_manager_cmd, "install"],
|
|
cwd=frontend_root,
|
|
)
|
|
if install_result.returncode != 0:
|
|
raise RuntimeError(
|
|
f"{package_manager_name} install failed; unable to start React UI"
|
|
)
|
|
if debug:
|
|
print("React frontend dependencies installed successfully")
|
|
|
|
return package_manager_cmd, package_manager_name
|
|
|
|
|
|
def launch_react_app(args) -> None:
|
|
"""Start the standalone React Web UI (Vite)."""
|
|
package_manager_cmd, package_manager_name = ensure_frontend_ready(debug=args.debug)
|
|
api_url = resolve_api_url(args.api_url)
|
|
display_host = resolve_browser_host(args.host)
|
|
|
|
env = os.environ.copy()
|
|
env["VITE_API_BASE_URL"] = api_url
|
|
env["CADDESIGNER_API_PROXY_TARGET"] = api_url
|
|
|
|
print("Starting CADDesigner React Web UI...")
|
|
print("=" * 60)
|
|
print(f"React UI address: http://{display_host}:{args.port}")
|
|
print(f"API proxy target: {api_url}")
|
|
print(f"Frontend package manager: {package_manager_name}")
|
|
print(f"Frontend directory: {frontend_root}")
|
|
if args.debug:
|
|
print("Debug mode: enabled")
|
|
print("Press Ctrl+C to stop the service")
|
|
print("=" * 60)
|
|
|
|
extra_args_separator = ["--"] if package_manager_name == "npm" else []
|
|
cmd = [
|
|
package_manager_cmd,
|
|
"run",
|
|
"dev",
|
|
*extra_args_separator,
|
|
"--host",
|
|
args.host,
|
|
"--port",
|
|
str(args.port),
|
|
"--strictPort",
|
|
]
|
|
|
|
os.chdir(frontend_root)
|
|
os.execvpe(package_manager_cmd, cmd, env)
|
|
|
|
|
|
def main():
|
|
"""Main function."""
|
|
parser = argparse.ArgumentParser(
|
|
description="CADDesigner React Web UI launcher",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Usage examples:
|
|
# Start React UI with the default configuration
|
|
%(prog)s
|
|
|
|
# Customize the listening address
|
|
%(prog)s --host 0.0.0.0 --port 7861
|
|
|
|
# Connect to a remote API server; React forwards requests through the proxy
|
|
%(prog)s --api-url http://192.168.1.100:8000
|
|
|
|
Environment variable:
|
|
CADDESIGNER_API_URL API server address (default: http://localhost:8000)
|
|
""",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--host",
|
|
default="0.0.0.0",
|
|
help="Web UI server host address (default: 0.0.0.0)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--port",
|
|
type=int,
|
|
default=7860,
|
|
help="Web UI server port (default: 7860)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--api-url",
|
|
default=None,
|
|
help="CADDesigner API server address (default: http://localhost:8000)",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--debug",
|
|
action="store_true",
|
|
help="Enable debug mode",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
launch_react_app(args)
|
|
except KeyboardInterrupt:
|
|
print("\nUser canceled; exiting...")
|
|
except Exception as exc:
|
|
print(f"Startup failed: {exc}")
|
|
if args.debug:
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|