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

452 lines
10 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 统一重构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 真正接上,再继续更深的结构治理。