From aebe281a0566a8a8cf2c207d483550234283c342 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 16 May 2026 15:06:53 +0800 Subject: [PATCH] docs: add batch 2 follow-up unification PRD --- ...tory-config-unification-batch2-followup.md | 404 ++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 prd-app-factory-config-unification-batch2-followup.md diff --git a/prd-app-factory-config-unification-batch2-followup.md b/prd-app-factory-config-unification-batch2-followup.md new file mode 100644 index 0000000..774b6b5 --- /dev/null +++ b/prd-app-factory-config-unification-batch2-followup.md @@ -0,0 +1,404 @@ +# PRD — App Factory / Config Unification Batch 2 Follow-up + +## 背景 + +基于 `origin/main` 最新代码的完整复审,Batch 2 已经完成了两项重要统一: + +1. 五个服务入口 `main.py` 已统一切到 `shared.app_factory.create_service_app(...)` +2. 五个服务配置 `config.py` 已基本统一切到 `shared.config_base` + +对应远端提交主线为: +- `14484ca` Add shared modules: app factory, config base, error handlers with tests +- `6d94b08` Refactor all apps to use shared config and app factory +- `378a4e5` Update home pages route and add test templates + +这说明 Batch 2 的方向是正确的,且公共启动逻辑已经完成第一轮收敛。 + +但完整复审后,当前状态还不能定义为“重构完成”。现有实现仍存在三类结构性残留: + +- 入口层和业务层仍大量依赖 `sys.path.insert(...)` 做导入兜底 +- `shared.app_factory` 已把 `slowapi` 变成统一启动依赖,但测试/安装契约没有同步补齐 +- home/admin 的认证守卫仍是服务内私有实现,没有进一步抽象为 shared guard helper,后续容易再次漂移 + +因此需要补一轮 follow-up 修正,目标不是继续大规模抽象,而是把 Batch 2 从“表面统一”推进到“结构上站稳”。 + +--- + +## 本轮目标 + +1. 消除本轮重构后最显著的导入技术债,减少 `sys.path.insert(...)` 残留 +2. 补齐 `slowapi` 的依赖契约与测试契约,确保 shared factory 在开发、测试、部署三条链路上一致 +3. 为 home/admin 当前的路由守卫模式定义统一抽象边界,避免未来各服务重复复制 `_require_auth(...)` +4. 补齐回归验证标准,让“通过”不再只表示 shared 单测通过,而是关键服务入口也能被实际导入/启动 + +--- + +## 非目标 + +本轮不做以下内容: + +- 不重写整站认证体系 +- 不把所有服务的 auth/token 校验一次性全量抽到 shared +- 不强行改成 Python package 完整安装式布局(如一次性引入 pyproject + editable install 作为唯一导入方案) +- 不改动业务权限模型本身 +- 不为所有历史测试做全面 fixture 重构 + +本轮只处理 Batch 2 复审中已经暴露、且会持续影响后续维护的最小必要问题。 + +--- + +## 发现的问题 + +### P0. shared factory 统一后,`slowapi` 变成强依赖,但测试/环境契约未同步 + +#### 现象 + +复审中执行: + +```bash +python -m pytest tests/test_security_hardening.py tests/test_frontend_backend_reuse_contract.py -q +``` + +结果通过: + +```text +13 passed +``` + +但执行 auth 侧 smoke tests: + +```bash +python -m pytest auth/tests/test_security_hardening.py auth/tests/test_login_redirect_flow.py -q +``` + +结果在 collection 阶段失败: + +```text +ModuleNotFoundError: No module named 'slowapi' +``` + +#### 根因 + +`shared.app_factory.py` 直接导入: + +- `from slowapi import _rate_limit_exceeded_handler` +- `from slowapi.errors import RateLimitExceeded` + +这意味着一旦服务入口统一切到 shared factory,`slowapi` 就不再是某些服务“可选依赖”,而是所有服务公共启动路径的硬依赖。 + +但是当前测试环境或依赖清单没有保证这一点,导致: + +- shared 层合同测试可以过 +- 真实服务入口测试却无法 import app + +这会制造“重构似乎完成,但服务级 smoke 根本没跑”的假阳性。 + +#### 需要修改 + +1. 检查并统一项目依赖声明,确保 `slowapi` 出现在实际安装入口中 + - 根 `requirements*.txt` / 服务依赖清单 / 部署安装脚本中必须一致 +2. 如果项目采用多入口安装方式,至少保证: + - 本地开发安装能拿到 `slowapi` + - CI / 测试安装能拿到 `slowapi` + - 生产部署安装能拿到 `slowapi` +3. 补一条最小 smoke 验证: + - 能成功 `import auth/src/main.py` 对应 app + - 能成功 `import home/src/main.py` 对应 app + +#### 验收标准 + +以下命令在标准开发环境中必须通过: + +```bash +python -m pytest tests/test_security_hardening.py tests/test_frontend_backend_reuse_contract.py -q +python -m pytest auth/tests/test_security_hardening.py auth/tests/test_login_redirect_flow.py -q +``` + +不允许再出现 `ModuleNotFoundError: slowapi`。 + +--- + +### P1. `sys.path.insert(...)` 残留过多,说明 shared 化尚未形成稳定导入边界 + +#### 现象 + +本轮复审中,目标文件本身仍保留路径注入: + +- `auth/src/config.py` +- `blog/src/config.py` +- `canvas/src/config.py` +- `prompt/src/config.py` +- `home/src/config.py` +- `auth/src/main.py` +- `blog/src/main.py` +- `canvas/src/main.py` +- `prompt/src/main.py` +- `home/src/main.py` + +此外,搜索结果表明更多 route / service / test 文件中也仍在重复同一模式。 + +#### 问题 + +这说明当前 shared 重构虽然抽走了公共逻辑,但**没有真正建立清晰的模块导入边界**。直接后果有三个: + +1. 各服务仍默认依赖“脚本式运行 + 手动改 sys.path” +2. 测试文件需要反复插 path 才能 import `src.*` +3. 后续任何目录结构变化,都会在多个服务同时引发脆弱导入问题 + +#### 本轮建议策略 + +不要一口气清理全仓所有 `sys.path.insert(...)`,而是先做“核心路径收缩”: + +##### 第一阶段:只处理 Batch 2 涉及的入口与配置 + +目标: +- 让五个 `main.py` +- 五个 `config.py` + +尽量不再各自复制路径注入模板。 + +推荐方案二选一: + +**方案 A:抽一个 shared import bootstrap helper** +- 新建如 `shared/imports.py` 或等价模块 +- 提供统一函数,例如: + - `ensure_project_root(__file__, levels=3)` +- 所有入口/配置文件统一调用这一 helper,而不是手写 `Path(...); if str(...) not in sys.path: sys.path.insert(...)` + +**方案 B:明确项目运行约定,减少入口内 path hack** +- 如果现有运行方式允许,以仓库根目录为 working directory 运行 uvicorn / pytest +- 配合测试配置修正 import path +- 让入口文件不再自己改 `sys.path` + +本轮更推荐 **方案 A**,原因: +- 改动小 +- 风险低 +- 不要求一次性重建整个包结构 +- 能先消掉最显眼的重复模板 + +##### 第二阶段:只在 follow-up 里列出,不要求本轮全做 + +- route 层残留 `sys.path.insert(...)` +- service 层残留 `sys.path.insert(...)` +- test 层残留 `sys.path.insert(...)` + +这部分可以作为下一轮技术债治理,不在本轮强制清仓。 + +#### 验收标准 + +至少以下 10 个文件不再各自手写重复的 `sys.path.insert(...)` 模板,或改为统一 helper: + +- `auth/src/config.py` +- `blog/src/config.py` +- `canvas/src/config.py` +- `prompt/src/config.py` +- `home/src/config.py` +- `auth/src/main.py` +- `blog/src/main.py` +- `canvas/src/main.py` +- `prompt/src/main.py` +- `home/src/main.py` + +并补一条测试/检查,验证这些入口仍可被正常导入。 + +--- + +### P1. home/admin 路由守卫真实存在,但仍是私有实现,后续易漂移 + +#### 现象 + +`home/src/routes/admin.py` 目前已经具备明确的服务端守卫逻辑: + +- `_require_auth(...)` 负责 cookie 用户校验 +- 未登录跳转到 auth 登录页,并携带 redirect +- 已登录但无权限时拒绝或回首页 +- service token 明确禁止访问 home admin + +这说明 ephron.ren 现状是: + +- 前台公开页无全局 guard +- 管理后台路由有服务端 guard + +这一方向本身是合理的。 + +#### 问题 + +现在的 guard 逻辑仍被实现为 `home` 服务内部私有函数,风险在于: + +1. 如果 blog/canvas/prompt 后续继续补 admin 规则,很容易各自再复制一份 `_require_auth(...)` +2. 登录跳转、redirect 处理、service token 禁入策略可能在不同服务间漂移 +3. 审计时很难快速确认“所有 admin 路由是否遵循同一守卫契约” + +#### 本轮要求 + +本轮不要求把所有服务的鉴权都重构完,但至少要把“守卫抽象边界”定下来。 + +推荐最小改法: + +1. 在 `shared/` 新增 admin guard helper(命名可调整) + - 负责未登录重定向的通用拼装 + - 负责 service token 是否允许访问 admin 的统一策略 + - 负责权限失败的统一响应约定 +2. `home/src/routes/admin.py` 改为消费 shared helper,而不是继续把完整策略内嵌在文件内部 +3. 先以 home 为落点,其他服务后续增量迁移 + +#### 范围边界 + +本轮 shared guard helper 不要求覆盖: +- 所有业务权限判断细节 +- 各服务特有的资源级校验 +- 所有服务 token 行为差异 + +只需要先统一“admin 入口守卫”的公共骨架。 + +#### 验收标准 + +- `home/src/routes/admin.py` 中不再保留大段内联守卫模板代码 +- 登录跳转、redirect、service token 禁止访问 admin 的公共逻辑进入 shared helper +- 现有 home admin 行为保持不变 + +--- + +### P2. 当前回归验证偏 shared 层,缺少“服务入口可导入”级别的合同测试 + +#### 现象 + +当前已经有的 shared/contract 测试能证明: +- 安全头策略存在 +- reuse contract 存在 + +但它们不能证明: +- 五个服务入口都还能成功 import +- shared app factory 的依赖在真实入口层可用 +- 各服务在统一 factory 下仍保持自己的 startup 责任 + +#### 需要补齐 + +新增一组轻量合同测试,目标不是测业务,而是测“入口仍然站得住”: + +建议至少覆盖: + +1. 五个服务入口可导入 + - `auth/src/main.py` + - `blog/src/main.py` + - `canvas/src/main.py` + - `prompt/src/main.py` + - `home/src/main.py` +2. 导入后存在 `app` +3. `app.routes` 中至少包含 `/health` +4. docs 开关、static mount、router 注入保留各自行为(可做轻量断言) + +#### 验收标准 + +新增测试在标准环境下通过,并能在 shared factory 依赖缺失、入口装配损坏、health route 丢失时给出明确失败信号。 + +--- + +## 建议实施顺序 + +### Task 1 — 补齐 `slowapi` 依赖契约 + +- 检查根依赖声明 +- 检查测试环境安装入口 +- 补齐缺失项 +- 跑 auth smoke tests 验证 + +### Task 2 — 收缩 Batch 2 目标文件中的 path hack + +- 先只处理五个 `main.py` +- 再处理五个 `config.py` +- 如果采用 shared helper,保证 helper 本身足够轻量,不引入新的循环依赖 + +### Task 3 — 提取 home admin guard 公共骨架 + +- 保留当前行为不变 +- 只抽公共流程,不重写权限模型 +- 先让 `home` 成为第一个 shared guard 使用者 + +### Task 4 — 补轻量入口合同测试 + +- 测入口 import +- 测 `/health` +- 测 shared factory 关键装配结果 + +--- + +## 建议涉及文件 + +### 必改 + +- `shared/app_factory.py` +- `shared/config_base.py`(如需辅助 import/helper,可一并评估) +- `auth/src/main.py` +- `blog/src/main.py` +- `canvas/src/main.py` +- `prompt/src/main.py` +- `home/src/main.py` +- `auth/src/config.py` +- `blog/src/config.py` +- `canvas/src/config.py` +- `prompt/src/config.py` +- `home/src/config.py` +- 依赖声明文件(按实际项目结构) + +### 可能新增 + +- `shared/imports.py` 或等价 helper +- `shared/admin_guard.py` 或等价 helper +- 新的入口合同测试文件 + +### 本轮只观察,不强制修改 + +- `home/src/services/auth.py` +- 其他 route/service/test 中的历史 `sys.path.insert(...)` + +--- + +## 验证命令 + +至少执行并记录以下命令: + +```bash +cd /home/ubuntu/ephron.ren +git fetch origin +python -m pytest tests/test_security_hardening.py tests/test_frontend_backend_reuse_contract.py -q +python -m pytest auth/tests/test_security_hardening.py auth/tests/test_login_redirect_flow.py -q +``` + +如果新增了入口合同测试,再补: + +```bash +python -m pytest tests/test_service_entrypoints.py -q +``` + +如有 home/admin 相关抽象变动,补对应最小验证: + +```bash +python -m pytest home/tests -q +``` + +--- + +## 完成定义 + +满足以下条件才算本轮完成: + +1. `slowapi` 不再导致服务入口测试 import 失败 +2. Batch 2 涉及的 10 个入口/配置文件不再各自复制同一段 path 注入模板,或至少统一为同一个 helper +3. home/admin 的公共守卫骨架进入 shared 层,当前行为无回归 +4. 新增轻量入口合同测试,能证明五个服务入口仍可正常装配 +5. 所有新增/修改测试通过 + +--- + +## 备注 + +本 PRD 的定位不是重新定义 Batch 2,而是对 Batch 2 做“完整复审后的结构补强”。 + +换句话说,前一轮已经解决“多个服务启动方式不统一”的问题;这一轮要解决的是: + +- 统一之后是否真的可维护 +- 统一之后测试是否真能覆盖到入口层 +- 统一之后 guard/导入边界是否足够稳定 + +这轮做完,才能把 Batch 2 从“完成了一次重构”提升为“建立了可持续的基础设施约束”。