diff --git a/SpXvScDiDT.txt b/SpXvScDiDT.txt new file mode 100644 index 0000000..fb0da11 --- /dev/null +++ b/SpXvScDiDT.txt @@ -0,0 +1 @@ +d813c7bbab566807a833fbc3b0e20a9c \ No newline at end of file diff --git a/dispatcher.py b/dispatcher.py new file mode 100644 index 0000000..507a3c9 --- /dev/null +++ b/dispatcher.py @@ -0,0 +1,63 @@ +"""server-core 统一入口(dispatcher)—— 对外生产服务 opc.pinesound.cn 单入口。 + +同一端口服务平台应用与培训子应用,按路径路由: + /api/*、/uploads/*、/SpXvScDiDT.txt → 培训子应用(app.training,保留 /api 路由与独立 data/opc.db) + 其余(/auth /opc /admin /agents 等)→ 平台应用(app.main,身份 + 业务域) + +启动(server-core 目录): + uv run uvicorn dispatcher:app --host 0.0.0.0 --port 8090 --reload +""" +from __future__ import annotations + +import logging + +from app.main import app as core_app +from app.training.main import app as training_app + +logger = logging.getLogger(__name__) + +ROUTE_TRAINING_PREFIXES = ("/api", "/uploads", "/SpXvScDiDT.txt") + + +class Dispatcher: + """极简 ASGI 分发器:驱动子应用 lifespan(核心库初始化)+ 按路径路由。""" + + def __init__(self, apps: list): + self._apps = apps + + async def _run_lifespan(self, receive, send): + """依次进入各子应用 lifespan(核心应用负责建表/种子),按启动/停止消息配对退出。""" + started: list = [] + try: + while True: + message = await receive() + if message["type"] == "lifespan.startup": + for a in self._apps: + cm = a.router.lifespan_context(app=a) + await cm.__aenter__() + started.append(cm) + await send({"type": "lifespan.startup.complete"}) + elif message["type"] == "lifespan.shutdown": + for cm in reversed(started): + await cm.__aexit__(None, None, None) + started = [] + await send({"type": "lifespan.shutdown.complete"}) + return + except BaseException: + for cm in reversed(started): + try: + await cm.__aexit__(None, None, None) + except Exception: # noqa: BLE001 + pass + raise + + async def __call__(self, scope, receive, send): + if scope["type"] == "lifespan": + await self._run_lifespan(receive, send) + return + path = scope.get("path", "") + target = training_app if path.startswith(ROUTE_TRAINING_PREFIXES) else core_app + await target(scope, receive, send) + + +app = Dispatcher([core_app, training_app])