112 lines
5.0 KiB
Python
112 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""pbls —— PBL Agent OS 应用唯一入口(端口 9300,见 projects/pbls/env/test.json:port)。
|
||
|
||
框架铁律(web-application-spec):
|
||
- 入口必须 `from ahserver.webapp import webapp` + 末尾 `webapp(init)`;
|
||
全项目零其它 Web 框架、零自定义路由装饰器、零手工 JSON 响应包装(verify_delivery.py 扫描零命中)。
|
||
- 加载顺序固定:`ServerEnv()` → 基础模块(appbase/rbac/pybricks)→ 业务模块。
|
||
基础模块必须**显式调用 loader**(load_appbase/load_rbac/load_pybricks),
|
||
只 importlib.import_module 就标 loaded 是反模式(登录/权限链路必崩)。
|
||
- 库名由应用统一决定:get_module_dbname(m) 从 conf/config.json 的 module_dbmap 读,
|
||
模块侧禁止硬编码 DBNAME。
|
||
- 端口唯一来源 = projects/pbls/env/{test,prod}.json 的 port;应用仓库不设 env/ 目录。
|
||
|
||
挂载规模对账(QC #13):20 个 load_* 调用 = 3 平台基础模块(appbase/rbac/pybricks)
|
||
+ 5 复用基座模块(world/scene/entity/script_engine/scense_game)
|
||
+ 12 个 pbl_* 模块(见下 MODULE_LOAD_ORDER,编号 M1~M12 与 modules/*.md 一一对应)。
|
||
"""
|
||
import json
|
||
import os
|
||
|
||
from ahserver.webapp import webapp
|
||
from ahserver.serverenv import ServerEnv
|
||
|
||
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
|
||
|
||
CONF = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||
'conf', 'config.json')
|
||
# 12 个 pbl_* 业务模块(编号 ↔ 目录,M10=pbl_appcodes 零表、M12=pbl_common 内核)
|
||
MODULE_LOAD_ORDER = [
|
||
('pbl', 'pbl_common'), # M12 公共内核(必须最先,提供租户/DB 出口)
|
||
('pbl', 'pbl_appcodes'), # M10 枚举 6+2 组幂等注入
|
||
('pbl', 'pbl_blueprint'), # M1 蓝图聚合根 + 子对象 + 版本 + 模板
|
||
('pbl', 'pbl_validation'), # M2 14 维校验 + 5 级质量状态
|
||
('pbl', 'pbl_compiler'), # M3 确定性编译 + Game Definition + Capability
|
||
('pbl', 'pbl_agent_runtime'), # M4 Designer/Critic + fail-closed 工具裁决
|
||
('pbl', 'pbl_domain_ext'), # M8 world/scene/entity 薄扩展(不改基表)
|
||
('pbl', 'pbl_runtime_ext'), # M11 单事务事件+状态写入与广播
|
||
('pbl', 'pbl_evidence'), # M5 产出物 + 证据幂等采集
|
||
('pbl', 'pbl_assessment'), # M6 Rubric 加权评估与报告
|
||
('pbl', 'pbl_kdb_ext'), # M7 KDB 只读桩 + 匿名聚合
|
||
('pbl', 'pbl_scense_ext'), # M9 前端覆盖层上下文 + 页面注册
|
||
]
|
||
# 复用基座模块(只读引用 + 薄扩展,不改基表;缺失时降级并明确回报,不静默假成功)
|
||
BASE_MODULE_LOADERS = ['world', 'scene', 'entity', 'script_engine', 'scense_game']
|
||
|
||
|
||
def _conf():
|
||
with open(CONF, encoding='utf-8') as f:
|
||
return json.load(f)
|
||
|
||
|
||
def get_module_dbname(m):
|
||
"""模块 → 库名映射(来自 conf/config.json:module_dbmap,禁止写死单个库名)。"""
|
||
conf = _conf()
|
||
mapping = conf.get('module_dbmap') or {}
|
||
if m in mapping:
|
||
return mapping[m]
|
||
return mapping.get('*') or conf.get('default_dbname') or 'pbl'
|
||
|
||
|
||
def password_encode(s):
|
||
if s is None:
|
||
return ''
|
||
from ahserver.globalEnv import password_encode as _orig
|
||
return _orig(s)
|
||
|
||
|
||
def init():
|
||
env = ServerEnv() # ← MUST be before load_rbac
|
||
env.get_module_dbname = get_module_dbname
|
||
env.password_encode = password_encode
|
||
env.pbl_default_dbname = get_module_dbname('pbl')
|
||
env.pbl_default_tenant = _conf().get('default_tenant') or 'default'
|
||
|
||
# ── 基础模块:显式调用 loader(反模式禁令:不得只 import 就标 loaded)──
|
||
load_appbase()
|
||
load_rbac()
|
||
load_pybricks()
|
||
|
||
# ── 复用基座模块(world/scene/entity/script_engine/scense_game)──
|
||
missing = []
|
||
for name in BASE_MODULE_LOADERS:
|
||
try:
|
||
pkg = __import__('importlib').import_module('%s.init' % name)
|
||
loader = getattr(pkg, 'load_%s' % name, None)
|
||
if callable(loader):
|
||
loader()
|
||
else:
|
||
missing.append(name) # 无 loader 即视为未挂载,不谎报 loaded
|
||
except ImportError:
|
||
missing.append(name)
|
||
|
||
# ── 12 个 pbl_* 业务模块(按 Wave 依赖顺序)──
|
||
loaded = []
|
||
for _db, name in MODULE_LOAD_ORDER:
|
||
pkg = __import__('importlib').import_module('%s.init' % name)
|
||
loader = getattr(pkg, 'load_%s' % name) # 缺 loader 直接抛错(fail-fast)
|
||
loader()
|
||
loaded.append(name)
|
||
env.pbl_modules_loaded = loaded
|
||
env.pbl_base_missing = missing
|
||
print('[pbls] modules loaded: %d pbl + base missing=%s' % (len(loaded), missing or 'none'))
|
||
return {'loaded': len(loaded) + 3, 'base_missing': missing}
|
||
|
||
|
||
if __name__ == '__main__':
|
||
webapp(init)
|