10 KiB
10 KiB
PRD: App Factory + Config 统一重构(PR1 Batch 2)
背景
PR1 Batch 1 已完成并通过验证,已落地:
shared/config_base.pyshared/error_handlers.pyshared/app_factory.pytests/test_shared_config_base.pytests/test_error_handlers.pytests/test_app_factory.py
当前 Batch 1 已验证通过,共 26 个测试通过,说明 shared 基础骨架已经可用。
接下来的 Batch 2 不再新增基础模块,而是把这些 shared 能力真正接入五个服务:
- 改造五个服务的
config.py - 改造五个服务的
main.py - 调整必要的兼容测试与行为验证
这一批是 PR1 中真正高风险的部分,因为它会影响现有服务启动与运行行为。
Batch 2 目标
本批次完成后,应该达到:
- 五个服务
config.py改为复用 shared config helper - 五个服务
main.py改为通过 shared app factory 创建 app home继续兼容历史ENVIRONMENT/health只保留统一实现,不产生重复注册- 外部行为保持兼容:
- URL 不变
- docs_url 行为不变
- static 挂载策略不被误改
- 404/500 页面行为兼容
非目标
这一批不做:
- 不收敛
sys.path.insert - 不改数据库 migration 归属
- 不改 service API
- 不调整目录结构
- 不统一所有 config 展示字段的内容
原因:这一批的核心任务是接入 shared 骨架,不是扩大范围做结构总清理。
批次顺序建议
为了降低回归风险,建议不要一次性全量乱序改。
先改 config.py
顺序建议:
auth/src/config.pyblog/src/config.pycanvas/src/config.pyprompt/src/config.pyhome/src/config.py最后
原因:
home有ENVIRONMENT历史兼容包袱,特殊性最强- 先处理标准服务,再处理兼容服务,排障更容易
再改 main.py
顺序建议:
canvas/src/main.pyprompt/src/main.pyblog/src/main.pyauth/src/main.pyhome/src/main.py最后
原因:
canvas和prompt相对简单blog/auth启动逻辑与历史 handler 更复杂home还涉及重复/health,最容易出兼容问题
Step 5:改造五个服务 config.py
涉及:
auth/src/config.pyblog/src/config.pycanvas/src/config.pyhome/src/config.pyprompt/src/config.py
目标:
- 删除重复的
.env/ required / optional / env mode helper - 改为复用
shared/config_base.py - 保持各服务专属字段与校验逻辑不变
5.1 auth/src/config.py
保留内容
AUTH_SECRET_KEYDATABASE_PATHCOOKIE_DOMAINCOOKIE_NAMETOKEN_MAX_AGEPROJECT_ROOTTEMPLATES_DIRvalidate_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_KEYTOKEN_MAX_AGECONTENT_DIRCACHE_DIRSEARCH_INDEX_DIRCOOKIE_NAMEPROJECT_ROOTSTATIC_DIRTEMPLATES_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_KEYTOKEN_MAX_AGECONTENT_DIRCOOKIE_NAMEPROJECT_ROOTTEMPLATES_DIR
改造方式
- 替换 env helper 为 shared helper
- 保留
CONTENT_DIR不存在时自动创建的行为
注意事项
- 不能把当前行为从“自动创建”改成“配置报错退出”
- 这点和 blog 不同,不能硬统一
5.4 prompt/src/config.py
保留内容
AUTH_SECRET_KEYTOKEN_MAX_AGEDATABASE_PATHPROJECT_ROOTTEMPLATES_DIRvalidate_config()
改造方式
- 替换 env helper 为 shared helper
- 暂时不扩大范围去彻底修 prompt 的 import 结构
注意事项
- 这一批只做 config 接入,不顺手改 import 体系
- 避免把单个 PR 变成“顺便半重构”
5.5 home/src/config.py
这是本批 config.py 里最需要小心的一个。
保留内容
STATIC_DIRTEMPLATES_DIRCOOKIE_NAMESERVICE_PORTAUTH_PORTAUTH_BASE_URLAUTH_LOGIN_URLvalidate_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.pyblog/src/main.pycanvas/src/main.pyhome/src/main.pyprompt/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_apirouters
要替换
- 手写
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 层验证
建议至少验证:
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 挂载策略不漂移
如本地可启动,建议:
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:
{"detail": "Internal Server Error"}
development 模式可额外包含:
{"detail": "Internal Server Error", "error": "..."}
这是合理改进,但应在实现说明中显式标注,避免被误解为无意行为变化。
4. home/src/routes/pages.py 的 /health 不能双留
如果 app factory 已统一提供 /health,就不能再保留 home pages 里的那份。
这是本批次最可能引发路由重复问题的点。
建议提交拆分
建议至少拆成 4 个 commit:
refactor: migrate auth/blog/canvas/prompt config modules to shared helpersrefactor: migrate home config module with ENVIRONMENT compatibilityrefactor: migrate canvas/prompt/blog/auth main modules to shared app factoryrefactor: 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 完成后,下一步就可以进入:
- PR1 收尾复查
- PR2:抽
shared/service_api/*与tests/helpers/* - PR3:处理 import 结构与
sys.path.insert - PR4:统一 DB schema / migration ownership
这才是合理的节奏:先把 shared app/config 真正接上,再继续更深的结构治理。