# 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 才算真正收口。