docs: add PRD for app factory/config unification batch 2
This commit is contained in:
452
prd-app-factory-config-unification-batch2.md
Normal file
452
prd-app-factory-config-unification-batch2.md
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
# PRD: App Factory + Config 统一重构(PR1 Batch 2)
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
PR1 Batch 1 已完成并通过验证,已落地:
|
||||||
|
|
||||||
|
- `shared/config_base.py`
|
||||||
|
- `shared/error_handlers.py`
|
||||||
|
- `shared/app_factory.py`
|
||||||
|
- `tests/test_shared_config_base.py`
|
||||||
|
- `tests/test_error_handlers.py`
|
||||||
|
- `tests/test_app_factory.py`
|
||||||
|
|
||||||
|
当前 Batch 1 已验证通过,共 **26 个测试通过**,说明 shared 基础骨架已经可用。
|
||||||
|
|
||||||
|
接下来的 Batch 2 不再新增基础模块,而是把这些 shared 能力**真正接入五个服务**:
|
||||||
|
|
||||||
|
1. 改造五个服务的 `config.py`
|
||||||
|
2. 改造五个服务的 `main.py`
|
||||||
|
3. 调整必要的兼容测试与行为验证
|
||||||
|
|
||||||
|
这一批是 PR1 中真正高风险的部分,因为它会影响现有服务启动与运行行为。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Batch 2 目标
|
||||||
|
|
||||||
|
本批次完成后,应该达到:
|
||||||
|
|
||||||
|
1. 五个服务 `config.py` 改为复用 shared config helper
|
||||||
|
2. 五个服务 `main.py` 改为通过 shared app factory 创建 app
|
||||||
|
3. `home` 继续兼容历史 `ENVIRONMENT`
|
||||||
|
4. `/health` 只保留统一实现,不产生重复注册
|
||||||
|
5. 外部行为保持兼容:
|
||||||
|
- URL 不变
|
||||||
|
- docs_url 行为不变
|
||||||
|
- static 挂载策略不被误改
|
||||||
|
- 404/500 页面行为兼容
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 非目标
|
||||||
|
|
||||||
|
这一批**不做**:
|
||||||
|
|
||||||
|
- 不收敛 `sys.path.insert`
|
||||||
|
- 不改数据库 migration 归属
|
||||||
|
- 不改 service API
|
||||||
|
- 不调整目录结构
|
||||||
|
- 不统一所有 config 展示字段的内容
|
||||||
|
|
||||||
|
原因:这一批的核心任务是**接入 shared 骨架**,不是扩大范围做结构总清理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 批次顺序建议
|
||||||
|
|
||||||
|
为了降低回归风险,建议不要一次性全量乱序改。
|
||||||
|
|
||||||
|
### 先改 `config.py`
|
||||||
|
顺序建议:
|
||||||
|
1. `auth/src/config.py`
|
||||||
|
2. `blog/src/config.py`
|
||||||
|
3. `canvas/src/config.py`
|
||||||
|
4. `prompt/src/config.py`
|
||||||
|
5. `home/src/config.py` 最后
|
||||||
|
|
||||||
|
原因:
|
||||||
|
- `home` 有 `ENVIRONMENT` 历史兼容包袱,特殊性最强
|
||||||
|
- 先处理标准服务,再处理兼容服务,排障更容易
|
||||||
|
|
||||||
|
### 再改 `main.py`
|
||||||
|
顺序建议:
|
||||||
|
1. `canvas/src/main.py`
|
||||||
|
2. `prompt/src/main.py`
|
||||||
|
3. `blog/src/main.py`
|
||||||
|
4. `auth/src/main.py`
|
||||||
|
5. `home/src/main.py` 最后
|
||||||
|
|
||||||
|
原因:
|
||||||
|
- `canvas` 和 `prompt` 相对简单
|
||||||
|
- `blog`/`auth` 启动逻辑与历史 handler 更复杂
|
||||||
|
- `home` 还涉及重复 `/health`,最容易出兼容问题
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5:改造五个服务 `config.py`
|
||||||
|
|
||||||
|
涉及:
|
||||||
|
- `auth/src/config.py`
|
||||||
|
- `blog/src/config.py`
|
||||||
|
- `canvas/src/config.py`
|
||||||
|
- `home/src/config.py`
|
||||||
|
- `prompt/src/config.py`
|
||||||
|
|
||||||
|
目标:
|
||||||
|
- 删除重复的 `.env` / required / optional / env mode helper
|
||||||
|
- 改为复用 `shared/config_base.py`
|
||||||
|
- 保持各服务专属字段与校验逻辑不变
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.1 `auth/src/config.py`
|
||||||
|
|
||||||
|
### 保留内容
|
||||||
|
- `AUTH_SECRET_KEY`
|
||||||
|
- `DATABASE_PATH`
|
||||||
|
- `COOKIE_DOMAIN`
|
||||||
|
- `COOKIE_NAME`
|
||||||
|
- `TOKEN_MAX_AGE`
|
||||||
|
- `PROJECT_ROOT`
|
||||||
|
- `TEMPLATES_DIR`
|
||||||
|
- `validate_config()`
|
||||||
|
|
||||||
|
### 改造方式
|
||||||
|
- 用 `load_service_env(_project_root)` 替换本地 `.env` 加载逻辑
|
||||||
|
- 用 `get_required_env()` / `get_optional_env()` 替换本地 helper
|
||||||
|
- 用 shared summary helper 统一打印机制
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- `validate_config()` 的行为不能漂移
|
||||||
|
- 现有 auth 的输出字段尽量保持一致,避免影响排障习惯
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.2 `blog/src/config.py`
|
||||||
|
|
||||||
|
### 保留内容
|
||||||
|
- `AUTH_SECRET_KEY`
|
||||||
|
- `TOKEN_MAX_AGE`
|
||||||
|
- `CONTENT_DIR`
|
||||||
|
- `CACHE_DIR`
|
||||||
|
- `SEARCH_INDEX_DIR`
|
||||||
|
- `COOKIE_NAME`
|
||||||
|
- `PROJECT_ROOT`
|
||||||
|
- `STATIC_DIR`
|
||||||
|
- `TEMPLATES_DIR`
|
||||||
|
|
||||||
|
### 改造方式
|
||||||
|
- 替换 env helper 为 shared helper
|
||||||
|
- 保留:
|
||||||
|
- `CONTENT_DIR` 存在性检查
|
||||||
|
- `CACHE_DIR.mkdir(...)`
|
||||||
|
- `SEARCH_INDEX_DIR.mkdir(...)`
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- 不要把目录初始化逻辑抽进 shared 层
|
||||||
|
- blog 目录语义应继续由 blog 自己负责
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.3 `canvas/src/config.py`
|
||||||
|
|
||||||
|
### 保留内容
|
||||||
|
- `AUTH_SECRET_KEY`
|
||||||
|
- `TOKEN_MAX_AGE`
|
||||||
|
- `CONTENT_DIR`
|
||||||
|
- `COOKIE_NAME`
|
||||||
|
- `PROJECT_ROOT`
|
||||||
|
- `TEMPLATES_DIR`
|
||||||
|
|
||||||
|
### 改造方式
|
||||||
|
- 替换 env helper 为 shared helper
|
||||||
|
- 保留 `CONTENT_DIR` 不存在时自动创建的行为
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- 不能把当前行为从“自动创建”改成“配置报错退出”
|
||||||
|
- 这点和 blog 不同,不能硬统一
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.4 `prompt/src/config.py`
|
||||||
|
|
||||||
|
### 保留内容
|
||||||
|
- `AUTH_SECRET_KEY`
|
||||||
|
- `TOKEN_MAX_AGE`
|
||||||
|
- `DATABASE_PATH`
|
||||||
|
- `PROJECT_ROOT`
|
||||||
|
- `TEMPLATES_DIR`
|
||||||
|
- `validate_config()`
|
||||||
|
|
||||||
|
### 改造方式
|
||||||
|
- 替换 env helper 为 shared helper
|
||||||
|
- 暂时不扩大范围去彻底修 prompt 的 import 结构
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- 这一批只做 config 接入,不顺手改 import 体系
|
||||||
|
- 避免把单个 PR 变成“顺便半重构”
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5.5 `home/src/config.py`
|
||||||
|
|
||||||
|
这是本批 `config.py` 里最需要小心的一个。
|
||||||
|
|
||||||
|
### 保留内容
|
||||||
|
- `STATIC_DIR`
|
||||||
|
- `TEMPLATES_DIR`
|
||||||
|
- `COOKIE_NAME`
|
||||||
|
- `SERVICE_PORT`
|
||||||
|
- `AUTH_PORT`
|
||||||
|
- `AUTH_BASE_URL`
|
||||||
|
- `AUTH_LOGIN_URL`
|
||||||
|
- `validate_config()`
|
||||||
|
|
||||||
|
### 改造方式
|
||||||
|
- 使用 `resolve_environment(legacy_key="ENVIRONMENT")`
|
||||||
|
- `ENV` 成为统一主字段
|
||||||
|
- 兼容历史 `ENVIRONMENT`
|
||||||
|
|
||||||
|
### 必测要求
|
||||||
|
- 只设置 `ENVIRONMENT=development` 时,仍进入开发模式
|
||||||
|
- 同时设置 `ENV=production` 与 `ENVIRONMENT=development` 时,以 `ENV` 为准
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- 这里不要用“看起来统一”破坏历史兼容
|
||||||
|
- `AUTH_BASE_URL` / `AUTH_LOGIN_URL` 逻辑必须保持不变
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 6:改造五个服务 `main.py`
|
||||||
|
|
||||||
|
涉及:
|
||||||
|
- `auth/src/main.py`
|
||||||
|
- `blog/src/main.py`
|
||||||
|
- `canvas/src/main.py`
|
||||||
|
- `home/src/main.py`
|
||||||
|
- `prompt/src/main.py`
|
||||||
|
|
||||||
|
目标:
|
||||||
|
- 删除重复的 app 创建与通用装配逻辑
|
||||||
|
- 改为统一通过 `shared.app_factory.create_service_app(...)`
|
||||||
|
|
||||||
|
每个服务应保留:
|
||||||
|
- service 专属 lifespan
|
||||||
|
- router 列表
|
||||||
|
- 标题/描述/version
|
||||||
|
- templates/static 路径
|
||||||
|
- service_name
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6.1 `canvas/src/main.py`
|
||||||
|
|
||||||
|
建议先改这个,因为最简单。
|
||||||
|
|
||||||
|
### 要保留
|
||||||
|
- `validate_config()`
|
||||||
|
- `print_config_summary()`
|
||||||
|
- canvas 专属 lifespan
|
||||||
|
- `pages/admin/service_api` routers
|
||||||
|
|
||||||
|
### 要替换
|
||||||
|
- 手写 `FastAPI(...)`
|
||||||
|
- 手写 `install_security_headers(app)`
|
||||||
|
- 手写 limiter 装配
|
||||||
|
- 手写 static mount
|
||||||
|
- 手写 error handlers
|
||||||
|
- 手写 `/health`
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- `static_dir` 传入时必须兼容“目录存在才挂载”策略
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6.2 `prompt/src/main.py`
|
||||||
|
|
||||||
|
### 要保留
|
||||||
|
- `init_db()`
|
||||||
|
- `print_config_summary()`
|
||||||
|
- prompt 专属 routers
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- app factory 只负责 app 壳,不负责 prompt 特有 db 初始化
|
||||||
|
- db 初始化仍放在 prompt 自己 lifespan 里
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6.3 `blog/src/main.py`
|
||||||
|
|
||||||
|
### 要保留
|
||||||
|
- search index 启动检查逻辑
|
||||||
|
- `print_config_summary()`
|
||||||
|
- blog 专属 routers
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- 这批不要顺手优化“搜索索引启动阻塞”问题
|
||||||
|
- 只做 app factory 接入,保持当前行为
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6.4 `auth/src/main.py`
|
||||||
|
|
||||||
|
### 要保留
|
||||||
|
- `init_db()`
|
||||||
|
- `_build_login_target()` 与 root redirect 逻辑
|
||||||
|
- `print_config_summary()`
|
||||||
|
- auth 专属 routers
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- auth 有较多 redirect / login 细节,这批只做 app 壳替换,不改 redirect 语义
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6.5 `home/src/main.py`
|
||||||
|
|
||||||
|
这是本批 `main.py` 中最需要小心的一个。
|
||||||
|
|
||||||
|
### 必做
|
||||||
|
- 将 app 创建迁到 factory
|
||||||
|
- 处理 `/health` 重复定义
|
||||||
|
|
||||||
|
### 推荐做法
|
||||||
|
- 删除 `home/src/routes/pages.py` 中的 `/health`
|
||||||
|
- 统一由 app factory 提供 `/health`
|
||||||
|
|
||||||
|
### 原因
|
||||||
|
如果两处都保留,会出现:
|
||||||
|
- 路由重复
|
||||||
|
- 行为漂移
|
||||||
|
- 后续 health 契约难统一
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
- `home` 是兼容包袱最多的服务,建议最后处理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 需要补的验证
|
||||||
|
|
||||||
|
## 一、config 层验证
|
||||||
|
建议至少验证:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pytest tests/test_security_hardening.py -q
|
||||||
|
python -m pytest auth/tests -q
|
||||||
|
python -m pytest blog/tests -q
|
||||||
|
python -m pytest canvas/tests -q
|
||||||
|
python -m pytest home/tests -q
|
||||||
|
python -m pytest prompt/tests -q
|
||||||
|
```
|
||||||
|
|
||||||
|
重点确认:
|
||||||
|
- `home` 的 `ENVIRONMENT` 兼容逻辑仍然成立
|
||||||
|
- 各服务 config import 后不会报错
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、main.py 接入验证
|
||||||
|
建议验证:
|
||||||
|
|
||||||
|
- 所有服务仍能创建 app
|
||||||
|
- dev 模式 `/docs` 仍可用
|
||||||
|
- `/health` 正常
|
||||||
|
- API 404 返回 JSON
|
||||||
|
- 页面 404/500 继续使用模板
|
||||||
|
- static 挂载策略不漂移
|
||||||
|
|
||||||
|
如本地可启动,建议:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python main.py --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
手动检查:
|
||||||
|
- home / auth / blog / canvas / prompt 均可启动
|
||||||
|
- 任意不存在 API 路径返回 JSON 404
|
||||||
|
- 任意不存在页面返回模板 404
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 必须特别注意的 4 个坑
|
||||||
|
|
||||||
|
### 1. 不要扩大范围改 import 结构
|
||||||
|
这批不要顺手去处理 `sys.path.insert`。
|
||||||
|
|
||||||
|
原因:
|
||||||
|
- 会扩大变更面
|
||||||
|
- 增加排障难度
|
||||||
|
- 混淆“是 app factory 接入问题,还是 import 重构问题”
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 不要把 `print_config_summary()` 内容硬统一
|
||||||
|
共享层只应统一**打印机制**,而不是强行让所有服务显示完全同样的字段。
|
||||||
|
|
||||||
|
原因:
|
||||||
|
- 每个服务关注的关键字段不同
|
||||||
|
- 一刀切会损失调试价值
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. API 500 JSON 化属于“标准化改进”,要显式说明
|
||||||
|
Batch 1 中 shared error handler 已将 API 500 规范成 JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"detail": "Internal Server Error"}
|
||||||
|
```
|
||||||
|
|
||||||
|
development 模式可额外包含:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"detail": "Internal Server Error", "error": "..."}
|
||||||
|
```
|
||||||
|
|
||||||
|
这是合理改进,但应在实现说明中显式标注,避免被误解为无意行为变化。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. `home/src/routes/pages.py` 的 `/health` 不能双留
|
||||||
|
如果 app factory 已统一提供 `/health`,就不能再保留 home pages 里的那份。
|
||||||
|
|
||||||
|
这是本批次最可能引发路由重复问题的点。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 建议提交拆分
|
||||||
|
|
||||||
|
建议至少拆成 4 个 commit:
|
||||||
|
|
||||||
|
1. `refactor: migrate auth/blog/canvas/prompt config modules to shared helpers`
|
||||||
|
2. `refactor: migrate home config module with ENVIRONMENT compatibility`
|
||||||
|
3. `refactor: migrate canvas/prompt/blog/auth main modules to shared app factory`
|
||||||
|
4. `refactor: migrate home main module to shared app factory and unify health endpoint`
|
||||||
|
|
||||||
|
这样更方便分批 review 和定位问题。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 完成定义
|
||||||
|
|
||||||
|
Batch 2 完成后,必须满足:
|
||||||
|
|
||||||
|
- [ ] 五个服务 `config.py` 已接入 `shared/config_base.py`
|
||||||
|
- [ ] 五个服务 `main.py` 已接入 `shared/app_factory.py`
|
||||||
|
- [ ] `home` 兼容 `ENVIRONMENT`
|
||||||
|
- [ ] `/health` 统一且无重复注册
|
||||||
|
- [ ] static 挂载策略保持兼容
|
||||||
|
- [ ] API 404/500 与页面 404/500 行为符合预期
|
||||||
|
- [ ] 所有相关测试通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后续建议
|
||||||
|
|
||||||
|
Batch 2 完成后,下一步就可以进入:
|
||||||
|
|
||||||
|
1. PR1 收尾复查
|
||||||
|
2. PR2:抽 `shared/service_api/*` 与 `tests/helpers/*`
|
||||||
|
3. PR3:处理 import 结构与 `sys.path.insert`
|
||||||
|
4. PR4:统一 DB schema / migration ownership
|
||||||
|
|
||||||
|
这才是合理的节奏:先把 shared app/config 真正接上,再继续更深的结构治理。
|
||||||
Reference in New Issue
Block a user