"""pbl_blueprint 模块 RBAC 路径注册脚本。 用途:把本模块全部新 API 路径 + 页面路径注册到平台 RBAC(权限路径表), 使 owner.admin / 租户管理员可授权,未注册路径一律 403。 执行方式(应用部署后一次性 / 幂等可重复执行): python -m pbl_blueprint.scripts.load_path 或在应用 init() 后由部署脚本调用 load_paths(env)。 """ import os import sys MODULE_NAME = 'pbl_blueprint' # ---- 本模块全部新 API 路径(与 api.py 的 API_PATHS 保持一致,单一事实源)---- API_PATHS = [ ('/pbl_blueprint/blueprint/create', 'POST', '新建蓝图'), ('/pbl_blueprint/blueprint/update', 'POST', '更新蓝图'), ('/pbl_blueprint/blueprint/delete', 'POST', '删除蓝图'), ('/pbl_blueprint/blueprint/get', 'GET', '蓝图详情'), ('/pbl_blueprint/blueprint/list', 'GET', '蓝图列表'), ('/pbl_blueprint/blueprint/tree', 'GET', '蓝图子对象树'), ('/pbl_blueprint/blueprint/fork', 'POST', '复制蓝图'), ('/pbl_blueprint/version/save', 'POST', '保存版本快照'), ('/pbl_blueprint/version/list', 'GET', '版本列表'), ('/pbl_blueprint/version/diff', 'GET', '版本对比'), ('/pbl_blueprint/version/rollback', 'POST', '版本回滚'), ('/pbl_blueprint/subobject/create', 'POST', '新建子对象'), ('/pbl_blueprint/subobject/update', 'POST', '更新子对象'), ('/pbl_blueprint/subobject/delete', 'POST', '删除子对象'), ('/pbl_blueprint/subobject/get', 'GET', '子对象详情'), ('/pbl_blueprint/subobject/list', 'GET', '子对象列表'), ('/pbl_blueprint/subobject/batch_upsert', 'POST', '批量写子对象'), ('/pbl_blueprint/subobject/types', 'GET', '子对象类型枚举'), ('/pbl_blueprint/template/create', 'POST', '新建模板'), ('/pbl_blueprint/template/update', 'POST', '更新模板'), ('/pbl_blueprint/template/delete', 'POST', '删除模板'), ('/pbl_blueprint/template/get', 'GET', '模板详情'), ('/pbl_blueprint/template/list', 'GET', '模板列表'), ('/pbl_blueprint/template/instantiate', 'POST', '模板实例化为蓝图'), ('/pbl_blueprint/template/offline_fallback', 'POST', '离线兜底生成蓝图'), ] # ---- 页面路径(wwwroot/*.ui 挂载点)---- PAGE_PATHS = [ ('/pbl_blueprint/index', 'GET', 'PBL蓝图管理主页'), ('/pbl_blueprint/blueprint/edit', 'GET', '蓝图编辑页'), ('/pbl_blueprint/template/list', 'GET', '蓝图模板库页'), ] # ---- 表级数据权限路径(CRUD 定义 json/*.json 对应)---- TABLE_PATHS = [ ('/pbl_blueprint/tbl/pbl_blueprint', 'CRUD', '蓝图主表数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_version', 'CRUD', '蓝图版本表数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_template', 'CRUD', '蓝图模板表数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_template_item', 'CRUD', '蓝图模板条目表数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_task', 'CRUD', '子对象-任务数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_mission', 'CRUD', '子对象-关卡数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_role', 'CRUD', '子对象-角色数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_learning_goal', 'CRUD', '子对象-学习目标数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_evidence_spec', 'CRUD', '子对象-证据规格数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_artifact_spec', 'CRUD', '子对象-产出物规格数据权限'), ('/pbl_blueprint/tbl/pbl_blueprint_reflection_spec', 'CRUD', '子对象-反思规格数据权限'), ] ALL_PATHS = API_PATHS + PAGE_PATHS + TABLE_PATHS def load_paths(env=None): """把 ALL_PATHS 注册进 RBAC(幂等:存在则更新描述,不存在则新增)。 :return: {'registered': n, 'skipped': m, 'failed': k} """ result = {'registered': 0, 'skipped': 0, 'failed': 0, 'paths': []} if env is None: try: from sage import ServerEnv env = ServerEnv() except Exception: env = None rbac = None if env is not None: for attr in ('rbac', 'RBAC'): rbac = getattr(env, attr, None) if rbac is not None: break if rbac is None: getter = getattr(env, 'get_rbac', None) if callable(getter): try: rbac = getter() except Exception: rbac = None for path, method, caption in ALL_PATHS: item = {'path': path, 'method': method, 'caption': caption, 'module': MODULE_NAME} result['paths'].append(item) if rbac is None: result['skipped'] += 1 continue try: registered = False for fn_name in ('register_path', 'add_path', 'load_path', 'register'): fn = getattr(rbac, fn_name, None) if callable(fn): try: fn(path, method, caption) except TypeError: fn(item) registered = True break if registered: result['registered'] += 1 else: result['skipped'] += 1 except Exception: result['failed'] += 1 return result def main(): sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) res = load_paths() print('[pbl_blueprint] load_path done: registered=%d skipped=%d failed=%d total=%d' % ( res['registered'], res['skipped'], res['failed'], len(res['paths']))) return 0 if __name__ == '__main__': sys.exit(main())