Files
ephron-ren-prd/prd-app-factory-config-unification-complete-checklist.md

538 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PRD — App Factory / Config Unification 完整优化清单
## 背景
`ephron.ren` 当前已经完成了一轮 app factory / config shared 化重构,远端相关主线提交为:
- `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
现状不是“完全没做”,而是:
- shared 基础设施已经建立
- 五个服务已经基本接入 shared app/config 骨架
- 但整体状态仍停留在“完成了第一轮统一”,还没达到“结构稳定、依赖完整、验证闭环、后续不易漂移”的完成态
因此这份 PRD 不再按 Batch 1 / Batch 2 / follow-up 拆分,而是直接给出 **从当前状态到完成态的完整优化清单**
这份文档的目标是:开发只看这一份,就知道哪些必须改、哪些建议改、哪些不要顺手乱改,以及最后怎么验收。
---
## 总体结论
当前重构方向是对的,但还差四类关键补完:
1. **依赖契约没补齐**
- `shared.app_factory` 已把 `slowapi` 变成公共硬依赖
- 但测试/安装链路没有完全同步,导致服务入口级测试会直接 import 失败
2. **导入边界不稳定**
- `main.py` / `config.py` 乃至 route/service/test 里仍保留大量 `sys.path.insert(...)`
- 说明 shared 化了逻辑,但没收住模块边界
3. **admin 路由守卫仍是服务内私有实现**
- `home` 已有实际 guard
- 但 guard 骨架没 shared 化,未来其他服务继续扩展时很容易复制漂移
4. **验证层只证明了 shared 能跑,没充分证明服务入口能跑**
- 当前 shared tests 通过,不代表五个服务入口都可稳定导入/装配
---
## 优化目标
完成本清单后,应该达到以下状态:
1. 五个服务的 app 创建与 config 读取,已经统一且可维护
2. shared app factory 的依赖在开发、测试、部署三条链路中一致成立
3. Batch 2 涉及的 10 个 `main.py/config.py` 文件不再各自复制路径注入模板
4. `home` 的 admin 守卫公共骨架进入 shared 层,后续可供其他服务复用
5. `/health` 只由 shared app factory 统一归口
6. 测试不仅覆盖 shared 合同,还覆盖服务入口装配合同
7. 改完后可以明确说:这轮统一已经从“代码抽取”升级为“工程约束已建立”
---
## 非目标
本轮不要做以下事情:
- 不重写整站认证体系
- 不全量重构所有 route/service/test 的导入结构
- 不一次性把所有 auth/token 逻辑全部抽进 shared
- 不改业务权限模型
- 不改 service API 设计
- 不迁移仓库目录结构
- 不强制一步到位改成完整 package 化(如全面 pyproject/installable layout
- 不顺手做与本次重构无关的业务修复
原则很简单:**只修 app/config 统一后暴露出来的结构性缺口,不把这轮工作膨胀成全仓重构。**
---
## 一、必须修改P0 / P1
## P0-1. 补齐 `slowapi` 依赖契约
### 问题
`shared.app_factory.py` 已直接依赖:
- `slowapi._rate_limit_exceeded_handler`
- `slowapi.errors.RateLimitExceeded`
这意味着:
- 只要服务入口用 shared factory
- `slowapi` 就不再是“某服务可选依赖”
- 而是整个服务启动路径的公共硬依赖
复审中的实际验证结果也说明了这个问题:
```bash
python -m pytest tests/test_security_hardening.py tests/test_frontend_backend_reuse_contract.py -q
```
通过;但执行:
```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'
```
### 必须改什么
1. 检查项目实际依赖入口
- 根依赖文件
- 测试依赖入口
- 部署安装入口
2. 保证 `slowapi` 在这三条链路里都被安装
3. 如果存在多个 requirements 文件,必须统一,不允许某条链路漏掉
4. 若 CI 有单独安装脚本,也必须同步更新
### 验收标准
以下命令在标准开发环境中必须通过:
```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
```
且不允许再出现:
```text
ModuleNotFoundError: slowapi
```
---
## P0-2. `/health` 必须只保留 shared app factory 单一归口
### 问题
app factory 统一后的设计目标之一,就是 `/health` 由 shared factory 统一注册。
这件事必须做到“单一所有权”,否则后续一旦 health 响应结构升级,很容易出现某个服务还是旧实现。
当前重点确认点:
- `home/src/routes/pages.py` 不应再重复定义 `/health`
- 其他服务也不应在页面路由层残留重复 health endpoint
### 必须改什么
1. 全仓检查 `/health` 定义位置
2. 保留 shared app factory 中的统一实现
3. 删除各服务 pages/routes 中的重复实现
4. 保证最终每个服务的 `/health` 都来自同一装配路径
### 验收标准
- 服务层页面路由文件中不再重复定义 `/health`
- `GET /health` 行为仍与现有外部契约兼容
- 相关 shared / entrypoint tests 通过
---
## P1-1. 收敛 Batch 2 目标文件中的 `sys.path.insert(...)` 残留
### 问题
本轮 shared 化后,以下 10 个核心文件本身仍保留路径注入模板:
- `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`
这说明当前统一只是“逻辑 shared 化”,不是“导入边界稳定化”。
### 必须改什么
本轮至少把这 10 个文件收住,不要求一次性清理全仓。
推荐两种方案里选一种:
#### 方案 A抽统一 import bootstrap helper推荐
例如新增:
- `shared/imports.py`
提供统一函数,例如:
```python
def ensure_project_root(file_path: str, levels: int = 3) -> None: ...
```
让入口/配置文件统一调用 helper不再各自复制
```python
project_root = Path(...)
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
```
#### 方案 B修正运行/测试约定,让入口文件不再自行改 path
如果当前项目运行方式允许,也可以通过统一 cwd / pytest path / uvicorn 启动约定来消除核心入口内的 path hack。
但从风险和改造成本看,本轮更推荐方案 A。
### 最低要求
至少保证上面列出的 10 个文件不再各自手写重复模板,哪怕先统一到一个 helper。
### 验收标准
- 10 个核心文件中不再散落重复的 path 注入模板
- 改完后这 10 个文件仍然可导入
- 不引入新的循环依赖
---
## P1-2. 将 home/admin 路由守卫的公共骨架提取到 shared
### 问题
当前 `home/src/routes/admin.py` 实际上已经有明确的服务端路由守卫:
- 未登录跳转 auth 登录页
- 带 redirect 返回
- 已登录但无权限则拒绝/跳转
- service token 禁止访问 home admin
这说明现在 **有 guard**,但它仍然是 `home` 服务内的私有实现。问题不在“有没有守卫”,而在“守卫骨架没有统一”。
如果未来 blog/canvas/prompt 继续加强 admin 规则,极容易再次复制 `_require_auth(...)` 模式,造成:
- 登录跳转策略漂移
- redirect 处理方式漂移
- service token admin 访问策略漂移
### 必须改什么
1.`shared/` 新增 admin guard helper命名可调整
2. helper 至少负责这三件事:
- 未登录时的登录跳转 URL 拼装
- service token 是否允许访问 admin 的统一策略
- 权限失败时的统一返回约定
3. `home/src/routes/admin.py` 改为消费 shared helper
4. 先落地 home不要求本轮同步改完所有服务 admin
### 不需要在本轮做的事
- 不要求把所有业务权限判断都抽进去
- 不要求统一所有服务特有的资源级权限逻辑
- 不要求一次性重写 blog/canvas/prompt 的 admin 认证结构
### 验收标准
- `home/src/routes/admin.py` 不再保留大段内联 guard 骨架模板
- shared helper 能表达登录跳转 / redirect / service token admin 禁入策略
- home 现有 admin 行为无回归
---
## P1-3. 补服务入口装配级合同测试
### 问题
当前 shared 测试通过,能证明:
- shared config base 基本可用
- shared error handlers 基本可用
- shared app factory 基本可用
但这不等于五个真实服务入口都还能导入、装配、挂 router、提供 `/health`
现在缺的是一类非常轻量但非常关键的测试:**服务入口装配合同测试**。
### 必须改什么
新增一组轻量测试,重点不是测业务,而是测“服务入口仍站得住”。
建议覆盖:
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 开关仍符合 dev/prod 预期
5. static mount / router 注册没有在统一重构中丢失
### 验收标准
新增测试能在这些情况给出明确失败信号:
- shared factory 依赖缺失
- 入口导入断裂
- `/health` 丢失
- app factory 装配未生效
---
## 二、建议修改P2
这些不是“本轮不改就一定阻断”的问题,但建议一起纳入开发检查表。
## P2-1. 统一依赖声明与文档说明
### 建议改什么
1. 在依赖文件中明确标注 shared app factory 的新增公共依赖
2. 若项目有 README / 开发说明,补一句:
- 使用 shared app factory 的服务,需要安装公共依赖集
3. 若有部署脚本或 bootstrap 脚本,检查是否与 requirements 同步
### 目的
避免开发者只看代码不看变更背景,导致本地能跑 shared tests、却跑不动服务 tests。
---
## P2-2. 为 `ENV` / `ENVIRONMENT` 兼容关系补显式测试
### 背景
当前 `home` 仍兼容历史 `ENVIRONMENT`,这是对的,但兼容行为需要被明确测试下来,否则后面很容易被“顺手清理”掉。
### 建议增加的断言
至少覆盖:
1. 只设置 `ENVIRONMENT=development` 时,`home` 仍进入开发模式
2. 同时设置 `ENV=production``ENVIRONMENT=development` 时,以 `ENV` 为准
3. 未设置时默认回落到 `production`
### 目的
把“历史兼容”从口头约定变成测试契约。
---
## P2-3. 统一 config summary 输出风格,但不要统一业务字段含义
### 建议改什么
1. 保持 summary 打印格式统一
2. 但不要为了“字段看起来一样”而强行让各服务展示完全同一组字段
### 原则
- 统一的是打印骨架
- 不是每个服务必须展示完全一样的业务字段
例如:
- `blog` 仍应展示 `CONTENT_DIR` / `CACHE_DIR`
- `home` 仍应展示 `AUTH_BASE_URL` / `SERVICE_PORT`
### 目的
避免“形式统一”侵入业务语义。
---
## P2-4. 为后续全仓导入治理保留清单,但不要本轮扩大范围
### 已确认存在的后续技术债
除了 10 个核心文件外route/service/test 层仍有较多 `sys.path.insert(...)`
### 建议处理方式
- 本轮只在 PRD 中登记为后续项
- 不要在这轮顺手扩展到全仓清理
### 原因
这类改动跨面太大,容易把“当前可收敛的重构收尾”演变成“无法控风险的大扫除”。
---
## 三、明确不要修改的内容
以下内容在本轮必须明确禁止“顺手改”:
1. **不要重写业务权限模型**
- 例如 owner/admin/user 的权限体系
- 不属于 app/config 统一收尾范围
2. **不要把所有 auth/token 校验逻辑一次性抽到 shared**
- 当前只要求抽 admin guard 公共骨架
- 不是全站认证大重构
3. **不要一口气清理所有 `sys.path.insert(...)`**
- 只收缩 Batch 2 涉及的核心入口/配置文件
- 其余留待后续技术债专项
4. **不要改 service API / 路由外部契约**
- URL 不变
- 响应形态不因为这轮统一而改变
5. **不要顺手改目录结构**
- 不迁移为全量 package layout
- 不移动服务目录
6. **不要把 config 层业务逻辑抽进 shared**
- 例如 blog 的缓存目录创建
- canvas 的 content 目录自动创建
- 这些仍应由各服务自己负责
---
## 四、建议实施顺序
虽然这份文档不再按阶段写,但实际落地仍建议按风险从低到高推进:
### 1. 先补依赖契约
- 先解决 `slowapi` 安装链路
- 否则后续很多入口测试都没法真实验证
### 2. 再补入口合同测试
- 先建立“导入/装配级失败能被看见”的机制
- 避免边改边盲飞
### 3. 再收敛 10 个核心文件的 path hack
- 控制范围,不碰全仓
### 4. 再抽 home/admin guard 骨架
- 这是结构优化,不是依赖修复,放在后面更稳
### 5. 最后全量回归
- shared tests
- auth smoke
- home tests
- 新增 entrypoint tests
---
## 五、建议涉及文件
## 必改文件
- `shared/app_factory.py`
- `shared/config_base.py`(如需补 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
- `tests/test_service_entrypoints.py` 或等价入口合同测试文件
## 只观察,不要求本轮修改
- `home/src/services/auth.py`
- 其他 route/service/test 中的历史 `sys.path.insert(...)`
- 任何 service API / 业务 handler 的功能逻辑
---
## 六、完整验收命令
至少执行并记录以下验证:
```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 guard helper再补
```bash
python -m pytest home/tests -q
```
如果有 shared 相关新增单测,也一并跑:
```bash
python -m pytest tests/test_shared_config_base.py tests/test_error_handlers.py tests/test_app_factory.py -q
```
---
## 七、完成定义
满足以下全部条件,才算这轮优化真正完成:
1. `slowapi` 在开发、测试、部署链路中都已被视为公共硬依赖
2. 服务入口级测试不再因 `slowapi` 缺失而 import 失败
3. `/health` 已统一归口到 shared app factory
4. Batch 2 涉及的 10 个核心 `main.py/config.py` 文件不再各自复制 path 注入模板,或至少统一到同一 helper
5. `home` 的 admin 路由守卫公共骨架已进入 shared 层
6. 新增服务入口装配合同测试,并实际通过
7. shared 层测试 + auth smoke + home 相关测试均通过
8. 外部行为未发生非预期变化:
- URL 不变
- docs_url 行为兼容
- static mount 行为兼容
- 404/500 页面行为兼容
---
## 八、一句话给开发的执行原则
这轮工作的本质不是“继续抽公共代码”,而是:
> 把已经做完的 shared 化,补成一套真正可依赖、可验证、可持续的工程约束。
判断标准也很明确:
- 不是“代码看起来更整洁了”
- 而是“入口真的能导入、依赖真的齐、guard 不再乱飘、测试真的能兜住回归”
做到这一点,这轮 app factory / config unification 才算真正收口。