first commit
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user