docs: add batch 2 follow-up unification PRD
This commit is contained in:
404
prd-app-factory-config-unification-batch2-followup.md
Normal file
404
prd-app-factory-config-unification-batch2-followup.md
Normal file
@@ -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 从“完成了一次重构”提升为“建立了可持续的基础设施约束”。
|
||||
Reference in New Issue
Block a user