CadHubManage/launcher.py
2025-12-22 09:14:34 +08:00

117 lines
3.3 KiB
Python

"""Executable entry point for CadHubManage."""
from __future__ import annotations
import argparse
import multiprocessing
import os
import shutil
import sys
from pathlib import Path
def _copy_missing_tree(src: Path, dst: Path) -> None:
if not src.exists():
return
for item in src.rglob("*"):
rel = item.relative_to(src)
target = dst / rel
if item.is_dir():
target.mkdir(parents=True, exist_ok=True)
continue
if target.exists():
continue
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(item, target)
def _materialize_packaged_resources(base_dir: Path) -> None:
if not getattr(sys, "frozen", False):
return
meipass = Path(getattr(sys, "_MEIPASS", ""))
if not meipass:
return
packaged_configs = meipass / "configs"
target_configs = base_dir / "configs"
_copy_missing_tree(packaged_configs, target_configs)
packaged_env_example = meipass / ".env.example"
target_env_example = base_dir / ".env.example"
if packaged_env_example.exists() and not target_env_example.exists():
shutil.copy2(packaged_env_example, target_env_example)
(base_dir / "logs" / "operation_logs").mkdir(parents=True, exist_ok=True)
def prepare_environment() -> Path:
raw_base_dir = os.environ.get("CADHUB_BASE_DIR")
if raw_base_dir:
base_dir = Path(raw_base_dir).resolve()
elif getattr(sys, "frozen", False):
base_dir = Path(sys.executable).resolve().parent
else:
base_dir = Path(__file__).resolve().parent
base_dir.mkdir(parents=True, exist_ok=True)
_materialize_packaged_resources(base_dir)
os.environ.setdefault("CADHUB_BASE_DIR", str(base_dir))
os.chdir(base_dir)
return base_dir
BASE_DIR = prepare_environment()
from app.main import app # noqa: E402 pylint: disable=wrong-import-position
from app.config import settings # noqa: E402 pylint: disable=wrong-import-position
import uvicorn # noqa: E402 pylint: disable=wrong-import-position
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="CadHubManage offline launcher")
parser.add_argument("--host", default=settings.host, help="Host address")
parser.add_argument("--port", type=int, default=settings.port, help="Port number")
parser.add_argument("--workers", type=int, default=1, help="Number of worker processes")
parser.add_argument("--reload", action="store_true", help="Enable auto-reload (development only)")
parser.add_argument(
"--log-level",
default=settings.log_level.lower(),
choices=["critical", "error", "warning", "info", "debug", "trace"],
help="Logging level",
)
return parser.parse_args()
def main() -> None:
args = parse_args()
if args.reload and args.workers != 1:
print("Reload mode requires workers=1, adjusting automatically.")
args.workers = 1
uvicorn_app = app
if args.reload or args.workers != 1:
uvicorn_app = "app.main:app"
uvicorn.run(
uvicorn_app,
host=args.host,
port=args.port,
reload=args.reload,
workers=args.workers,
log_level=args.log_level,
)
if __name__ == "__main__":
multiprocessing.freeze_support()
main()