上一条 commit 702f274 归档 24 个自造中间件时,连带把 ns_wiring.py 一并归档,导致 rbac/appbase 的 dspy 端点报 TypeError: 'NoneType' object is not callable(模块函数未注入命名空间)。 ns_wiring 不属于鉴权中间件,它是「业务模块 dispatch + 只读基础模块 handler 补齐」接线:只在运行时 setattr env.<handler>,不装配 aiohttp 中间件、不改基础模块源码,因此恢复。 实测(184 个 dspy 语法扫描 + 端点扫描): 恢复前:19 个端点 500(list_path_roles / get_code / find_unauth_files 等) 恢复后:仅剩「无参 GET 调写端点」的假阳性,只读端点全部正常 ns_wiring.py 从 app/_archive_disabled_20261028_151505/ 原样恢复,未改动。
125 lines
4.0 KiB
Python
125 lines
4.0 KiB
Python
"""唤景平台世界模式(scense)——应用唯一入口。
|
||
|
||
一个应用 = 一个入口 = 一个端口。
|
||
标准 ahserver + rbac 提供注册/登录/会话管理,应用不自行实现鉴权中间件。
|
||
"""
|
||
from ahserver.webapp import webapp
|
||
from ahserver.serverenv import ServerEnv
|
||
|
||
import logging
|
||
import os
|
||
import sys
|
||
|
||
import bricks_for_python # registers bui processor
|
||
from bricks_for_python.init import load_pybricks # registers UiWindow etc.
|
||
from appbase.init import load_appbase
|
||
from rbac.init import load_rbac
|
||
|
||
log = logging.getLogger("scense.entry")
|
||
|
||
_APP_DIR = os.path.dirname(os.path.abspath(__file__))
|
||
_DEPLOY_DIR = os.path.dirname(_APP_DIR)
|
||
for _p in (_APP_DIR, _DEPLOY_DIR):
|
||
while _p in sys.path:
|
||
sys.path.remove(_p)
|
||
sys.path.insert(0, _DEPLOY_DIR)
|
||
sys.path.append(_APP_DIR)
|
||
|
||
|
||
def get_module_dbname(m):
|
||
"""返回模块 m 对应的数据库名(本项目单库:全部落 scense)。"""
|
||
return "scense"
|
||
|
||
|
||
def password_encode(s):
|
||
if s is None:
|
||
return ""
|
||
from ahserver.globalEnv import password_encode as _orig
|
||
return _orig(s)
|
||
|
||
|
||
def _healthz(request, params_kw, *args, **kw):
|
||
"""/healthz 健康探针(标准 ahserver FunctionProcessor 注册,config.website.startswiths)。"""
|
||
from aiohttp import web
|
||
return web.json_response({
|
||
"ok": True,
|
||
"status": 200,
|
||
"app": "scense",
|
||
"entry": "app/scense.py",
|
||
})
|
||
|
||
|
||
def _drive(_aw):
|
||
"""驱动异步加载器(entity.init.load_entity / world_snapshot.init.load_world_snapshot 是 async def)。"""
|
||
if not hasattr(_aw, "__await__"):
|
||
return _aw
|
||
import asyncio
|
||
try:
|
||
asyncio.get_running_loop()
|
||
except RuntimeError:
|
||
return asyncio.run(_aw)
|
||
import concurrent.futures
|
||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as _ex:
|
||
return _ex.submit(lambda: asyncio.run(_aw)).result()
|
||
|
||
|
||
def init():
|
||
# `python app/scense.py` 会把脚本目录置于 sys.path[0],遮蔽同名业务模块包
|
||
_script_dir = os.path.join(_DEPLOY_DIR, "app")
|
||
while _script_dir in sys.path:
|
||
sys.path.remove(_script_dir)
|
||
sys.path.append(_script_dir)
|
||
if _DEPLOY_DIR not in sys.path:
|
||
sys.path.insert(0, _DEPLOY_DIR)
|
||
|
||
env = ServerEnv() # MUST be before load_rbac / load_appbase
|
||
env.get_module_dbname = get_module_dbname
|
||
env.password_encode = password_encode
|
||
|
||
load_appbase()
|
||
load_rbac()
|
||
load_pybricks()
|
||
|
||
# ── 业务模块导入(一个入口一个端口,所有模块挂在这里)──
|
||
from world.init import load_world
|
||
from scene.init import load_scene
|
||
from entity.init import load_entity
|
||
from script_engine.init import load_script_engine
|
||
from world_snapshot.init import load_world_snapshot
|
||
from world_sync.init import load_world_sync
|
||
from scense_demo.init import load_demo
|
||
from scense.init import load_scense
|
||
from scense_game.init import load_scense_game
|
||
from scense_drag.init import load_drag
|
||
from runtime.init import load_runtime
|
||
|
||
load_world()
|
||
load_scene()
|
||
_drive(load_entity())
|
||
load_script_engine()
|
||
_drive(load_world_snapshot())
|
||
load_world_sync()
|
||
load_demo(env)
|
||
load_scense(env)
|
||
load_scense_game(env)
|
||
load_drag()
|
||
load_runtime(env)
|
||
|
||
# ── 命名空间接线(业务模块 dispatch + 只读基础模块 handler 补齐)──
|
||
# rbac/appbase/bricks 是只读基础模块,其 dspy 依赖 env.<handler>;
|
||
# 未挂载时表现为 TypeError: 'NoneType' object is not callable。
|
||
# 只在运行时 setattr,不改基础模块源码。
|
||
try:
|
||
from app.ns_wiring import wire_ns_shim
|
||
wire_ns_shim(env)
|
||
except Exception as _exc:
|
||
import logging as _logging
|
||
_logging.getLogger(__name__).error("[ns-wiring] wire_ns_shim failed: %s", _exc)
|
||
|
||
# ── 标准函数处理器注册(配合 conf/config.json website.startswiths)──
|
||
from appPublic.registerfunction import RegisterFunction
|
||
RegisterFunction().register("healthz", _healthz)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
webapp(init) |