chore: Phase 2 收尾——平台层完成(审计+安全自查+文档+清理单)
- 全量回归 566 passed(根目录与 tests/ 双跑一致),基线数字刷新(459→566) - 零同步 IO 审计复查:app/ 无 requests/urllib/time.sleep/open,scrypt CPU 注记在案 - 安全自查全过:platform 层零 logger(凭据不进日志)、gitignore 覆盖实证 (tenants/*.db* / platform.db* / data/)、登录限流在 /api/platform 豁免 路径仍生效(豁免只跳过租户解析,测试钉位在)、上传目录不可执行 (魔数+uuid 重命名,avatar 下发防路径穿透,content-type 按扩展名映射) - CLAUDE.md:新增 Phase 2 完成状态(基线/审计/安全/平台层能力一览/ admin API 速览)、Phase 2 沉淀取舍 10 项 + Phase 3 清理单(旧 12 项中 #1#2#3 Task 10、#10 Task 7 已完成划除)、Phase 4 部署记档、 Phase 3 计划编写注意(conftest 三坑 + db_source_app/super_client 夹具 + config_views 单入口 + stats 口径表) - README:现状刷新至 Phase 2 收尾时点 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
|
||||
## Phase 1 完成状态(2026-10-12 收尾核对)
|
||||
|
||||
**测试基线:459 passed**(根目录与 tests/ 子目录各跑一遍,数字一致;唯一 warning 为 starlette TestClient 对 httpx 的弃用提示,无害)。收尾提交前基线即此数,后续任务回归以此为准。
|
||||
**测试基线:459 passed**(根目录与 tests/ 子目录各跑一遍,数字一致;唯一 warning 为 starlette TestClient 对 httpx 的弃用提示,无害)。此为 Phase 1 时点数字;现行回归基线以 Phase 2 完成状态段为准(566)。
|
||||
|
||||
**零同步 IO 审计**(`grep -rn "requests\.\|urllib\|time\.sleep" app/`):
|
||||
|
||||
@@ -39,28 +39,86 @@
|
||||
|
||||
**当前能力一览**:多租户引擎(TenantRuntime 隔离边界,engine 不感知 platform);站点模型(tenant_config.Site,一租户多站点覆盖);词表/提示词/品牌全配置化(seed_profiles 出厂包 + 文件级 registry_reload 热加载,Phase 2 升级为 platform.db);Host 路由(host_middleware,未知 Host 404);WS 防串库(租户化 client key + 会话站点标);WS 每 IP 连接上限(conn_limiter,租户级 ws_conn_limit 配置)。
|
||||
|
||||
## Phase 2 清理单(前序审查挂账,集中列此)
|
||||
## Phase 2 完成状态(2026-10-13 收尾核对)
|
||||
|
||||
1. **FakeAI 三份拷贝收敛进 conftest**——下次吸收测试时先做(出处:上游吸收审查,tests/ 内多处 FakeAI 定义重复)。
|
||||
2. **rate_limit 与 ws_conn_limit 的 0 语义统一 + 配置类型校验**——试金石批次(出处:配置接线审查)。
|
||||
3. **name_to_region_extras / context_seed_map 等散装 resolver 收拢为 config_views 单入口**(出处:配置接线审查"种子映射消双路径"遗留)。
|
||||
4. **AIService.from_runtime 类方法**——组装正典化(出处:引擎改造审查)。
|
||||
5. **boundary_filter 视图常量退役**——admin 重写后旧测试迁移时(出处:引擎改造审查)。
|
||||
6. **push enabled 布尔空槽语义**——admin 配置显式化时解决(出处:push 审查)。
|
||||
7. **contact.template_languages 运行期接线**——后台批次(出处:配置 schema 审查)。
|
||||
8. **话术含引号/换行的转义**——编辑器批次(`_format_deflection_pool` docstring 有注)。
|
||||
9. **产品决策项**:国名纠正 e164 变化时是否补发更正邮件(上游行为更优);REST/WS 服务端 IP 口径是否统一(出处:上游吸收审查)。
|
||||
10. **avatar 从 seed_profiles 迁 uploads 卷**——上传功能落地时(出处:品牌下发审查)。
|
||||
11. **/health 分级健康**——Phase 4 部署批次(进程存活之外按依赖分级上报,健康检查可区分"进程活"与"依赖通";出处:终审审查 M-3)。
|
||||
12. **PromptProvider.from_dir 的 FileNotFoundError 带路径上下文**——提示词目录缺失时异常信息含实际路径,定位不用猜(出处:终审审查 M-4)。
|
||||
**测试基线:566 passed**(根目录与 tests/ 子目录各跑一遍,数字一致;唯一 warning 同 Phase 1——starlette TestClient 对 httpx 的弃用提示,无害)。后续任务回归以此为准。
|
||||
|
||||
## Phase 2 计划编写注意
|
||||
**零同步 IO 审计复查**(grep requests / urllib / time.sleep / open 于 app/):命中数 **0**——无 requests/urllib 导入、无 time.sleep、无 open() 调用;`import time` 仅 platform/auth.py 与 engine/services/rate_limit.py 两处,均只读 time.time() 时钟。scrypt 登录路径 CPU 开销注记在案(auth.py 模块 docstring:单次 ~50ms,只在登录/改密路径,不在每请求热路径——依赖只查内存 token 表)。
|
||||
|
||||
- conftest 跨循环纪律:`async_db` 夹具用 NullPool;TestClient 必须走 portal 桥接事件循环;禁止 `asyncio.run` 直开租户 session_factory(三个坑都在 tests/conftest.py 有既成解法,照抄勿发明)。
|
||||
- `build_app` / `app_client` / `TenantTestClient` 夹具已就绪,新测试优先复用而非自建 app 实例。
|
||||
**安全自查**(读码 + 定向测试 28 条全绿:test_auth / test_admin_mounting / test_avatar_upload / test_host_routing):
|
||||
|
||||
- **密码/token 不进日志**:app/platform/ 全层**零 logger 调用**(干净由构造保证);main.py 启动日志仅静态串 + slug/db 路径;引擎层(Phase 1 既有)日志参数为会话内容摘录/session_id/notify_emails,无凭据字段。
|
||||
- **gitignore 覆盖复核**(git check-ignore 实证全命中):`tenants/*.db{,-wal,-shm}`(租户库实际落 `tenants/<slug>.db`)、`platform.db{,-wal,-shm}`(仓库根)、`data/`(含 `data/uploads/` 上传卷)。
|
||||
- **登录限流在 /api/platform 豁免路径仍生效**:host_middleware 对 `/api/platform/` 前缀的豁免只跳过**租户解析**,认证守护在路由依赖;登录限流先于密码验证(锁定期内正确密码也 429)。测试钉位:test_auth.py::test_login_lockout_after_5_fails + test_admin_mounting.py::test_platform_api_bypasses_tenant_resolution / test_platform_prefix_not_open / test_prefix_match_precise。
|
||||
- **上传目录不可执行**:上传只认魔数(png/jpg/webp)+ uuid 重命名落盘——.py 文件无进无出;avatar 下发(public.py)取 PurePosixPath.name 并显式拒绝 `/` `\` 与目录点(防 Windows 反斜杠穿透),FileResponse content-type 由扩展名映射(只可能 image/*)。
|
||||
|
||||
**平台层能力一览**(Phase 2 交付;engine 仍不感知 platform):
|
||||
|
||||
- **认证**:scrypt 版本化哈希(stdlib 零新依赖,偏离 spec bcrypt/argon2 有注记)、内存 token 8h TTL、登录限流(username+IP 滑动窗 5 次/15 分钟)、改密(must_change_password 引导)、角色两级(超管 / 租户管理员)+ 409 语义。
|
||||
- **租户管理**:创建向导(消费出厂档案,blank=jc 墙提示词)、站点与域名子资源 CRUD、停用(聊天面 503、后台仍可见)、slug 不可变。
|
||||
- **配置版本管线**:分组校验 → 整份版本快照(MAX+1)→ 回滚 / 重置出厂 / 试金石(wall/test)→ registry_reload 热生效。
|
||||
- **上传**:头像 multipart(魔数校验 / 2MB 上限 / uuid 重命名),写配置版本 + 三级解析链下发(uploads → seed 资产 → 静态兜底)。
|
||||
- **数据视图**:会话列表(筛选/分页)/ 详情 / 删除 + 推送日志——租户 scope 强制(跨租户 404)。
|
||||
- **统计**:卡片 / 漏斗 / 趋势 / 分布 / 推送健康——逐租户库查询 + 应用层合并(**禁 ATTACH**,spec §7.6);指标口径表定死在 stats.py docstring。
|
||||
- **DB 源生产装配**:TENANT_SOURCE=db 默认(DbTenantSource),FileTenantSource 保留为测试/档案/冷备形态;bootstrap_platform.py 一键引导。
|
||||
|
||||
**admin API 速览**(全部 `/api/platform` 前缀;Host 中间件豁免该前缀的租户解析,认证由路由依赖守护——require_super 超管 / _tenant_guard 租户管理员):
|
||||
|
||||
- 认证:`POST /auth/login`、`POST /auth/logout`、`POST /auth/change-password`
|
||||
- 租户:`GET|POST /tenants`、`GET|PATCH /tenants/{slug}`、`POST /tenants/{slug}/sites`、`PATCH /tenants/{slug}/sites/{site_slug}`、`POST|DELETE /tenants/{slug}/sites/{site_slug}/domains[/{domain}]`
|
||||
- 配置:`GET /tenants/{slug}/config`、`PUT /tenants/{slug}/config/{group}`、`POST /tenants/{slug}/config/{group}/reset`、`GET /tenants/{slug}/config/versions[/{version}]`、`POST /tenants/{slug}/config/rollback`、`POST /tenants/{slug}/wall/test`
|
||||
- 上传:`POST /tenants/{slug}/brand/avatar`
|
||||
- 数据:`GET /tenants/{slug}/conversations[/{conv_id}]`、`DELETE /tenants/{slug}/conversations/{conv_id}`、`GET /tenants/{slug}/push-logs`
|
||||
- 统计:`GET /stats`(超管,`?tenant=` 单租户下钻)、`GET /tenants/{slug}/stats`
|
||||
- 平台设置:`GET|PUT /settings`
|
||||
|
||||
## Phase 2 沉淀的取舍与 Phase 3 清理单
|
||||
|
||||
**实现取舍(API 冻结 / Phase 3 前端对接前须知)**:
|
||||
|
||||
1. **PUT config 整组替换语义**——前端必须整组读-改-写(Task 6;做部分更新需先 GET 全组再改再 PUT)。
|
||||
2. **rollback 绕过 prefix 跨租户查重**(域名类校验只在 PUT 路径)——如需收口,在 rollback 前补跑查重。
|
||||
3. **并发保存同租户撞 UNIQUE → 500**(单管理员场景可接受;收口 = 捕 IntegrityError 转 409)。
|
||||
4. **统计 API 无 `?site=` 筛选**(spec §7.6 提及;Phase 3 前端需要时补)。
|
||||
5. **登录限流反代形态退化为按用户名**(限流键 username + request.client.host,无 XFF 解析;可信代理解析归部署批次)。
|
||||
6. **avatar 旧文件不清理**(换头像后旧文件留盘;孤儿累积无害)。
|
||||
7. **停用租户管理员存量 token 最长存活 8h**(TTL 限窗)——即时锁死需在 validate 时回查租户状态。
|
||||
8. **400(预检撞车)vs 409(竞态 IntegrityError)并存**——API 冻结前统一。
|
||||
9. **limiter 全量清扫**(token/登录限流的内存键永不回访,长跑进程缓慢累积;收口 = 定期清扫)。
|
||||
10. **lead_push_service 的 settings re-export 锚**(app.engine.config 的 settings 在 lead_push_service 顶部 re-export,22 处测试 monkeypatch 依赖此锚——改动前先迁测试)。
|
||||
|
||||
**Phase 3 清理单**(Phase 1/2 审查挂账滚动合并;~~删除线~~ 项已在 Phase 2 完成):
|
||||
|
||||
1. ~~FakeAI 三份拷贝收敛进 conftest~~(Task 10 完成:conftest.py 单一定义,tests/ 零拷贝)。
|
||||
2. ~~rate_limit 与 ws_conn_limit 的 0 语义统一 + 配置类型校验~~(Task 10 完成)。
|
||||
3. ~~name_to_region_extras / context_seed_map 等散装 resolver 收拢为 config_views 单入口~~(Task 10 完成:app/platform/config_views.py)。
|
||||
4. ~~avatar 从 seed_profiles 迁 uploads 卷~~(Task 7 完成:上传 + 三级解析链)。
|
||||
5. **AIService.from_runtime 类方法**——组装正典化(出处:引擎改造审查)。
|
||||
6. **boundary_filter 视图常量退役**——admin 重写后旧测试迁移时(出处:引擎改造审查)。
|
||||
7. **push enabled 布尔空槽语义**——admin 配置显式化时解决(出处:push 审查)。
|
||||
8. **contact.template_languages 运行期接线**——后台批次(出处:配置 schema 审查)。
|
||||
9. **话术含引号/换行的转义**——编辑器批次(`_format_deflection_pool` docstring 有注)。
|
||||
10. **产品决策项**:国名纠正 e164 变化时是否补发更正邮件(上游行为更优);REST/WS 服务端 IP 口径是否统一(出处:上游吸收审查)。
|
||||
11. **/health 分级健康**——Phase 4 部署批次(进程存活之外按依赖分级上报;出处:终审审查 M-3)。
|
||||
12. **PromptProvider.from_dir 的 FileNotFoundError 带路径上下文**——提示词目录缺失时异常信息含实际路径(出处:终审审查 M-4)。
|
||||
|
||||
## Phase 3 计划编写注意
|
||||
|
||||
- conftest 三坑纪律延续:`async_db` 夹具用 NullPool;TestClient 必须走 portal 桥接事件循环;禁止 `asyncio.run` 直开租户 session_factory(三个坑都在 tests/conftest.py 有既成解法,照抄勿发明)。
|
||||
- `build_app` / `app_client` / `TenantTestClient` 夹具已就绪,新测试优先复用而非自建 app 实例;**平台层测试用 `db_source_app` / `super_client` 夹具**(conftest.py:DB 源装配 + 超管登录态客户端,Phase 2 全部平台路由测试的既成姿势)。
|
||||
- 引擎侧读配置一律经 **config_views 单入口**(app/platform/config_views.py:resolve_email/push/ai_settings、resolve_contact_flags、context_seed_map、name_to_region_extras)——新消费点不再自写 resolver。
|
||||
- 统计前端照 **stats.py docstring 的指标口径表**展示(会话数不要求有消息、线索=三选一非空、推送成功率分母口径、北京日界趋势等,歧义处已定死)。
|
||||
|
||||
## Phase 4 部署记档(提前锚定,部署批次展开成 docs/deploy-production.md)
|
||||
|
||||
- **`pip install -r requirements.txt` 必须执行**:python-multipart(>=0.0.20)为 Phase 2 新依赖——Task 7 实证缺失即头像上传 500(FastAPI multipart 解析依赖它)。
|
||||
- **bootstrap_platform.py 用法**:`PYTHONUTF8=1 python scripts/bootstrap_platform.py [--db PATH] [--profiles SLUG ...] [--seed DIR]`。两步全幂等:init(schema + 超管引导,凭据取 env/.env 的 ADMIN_USERNAME/ADMIN_PASSWORD)+ 导入出厂租户(默认 auto jiancai 两租户,已有 slug 跳过);**不建租户管理员**(走后台创建向导补)。
|
||||
- **TENANT_SOURCE=db 默认**(生产装配形态);`file` 为冷备/本地调试回退。
|
||||
- **admin 域名反代区分**:`/api/platform/*` 前缀豁免租户 Host 解析——平台后台 API 可与租户聊天域名同源反代(按路径分流)或独立域名,两形态皆可;租户聊天面(`/`、`/static`、WS、`/api/public`、`/api/chat*`)仍严格按 Host 解析租户,未知 Host 404。
|
||||
|
||||
## 文档索引
|
||||
|
||||
- 设计文档(spec):`docs/superpowers/specs/2026-10-12-unified-kefu-platform-design.md`
|
||||
- Phase 1 实施计划:`docs/superpowers/plans/2026-10-12-phase1-engine-merge-tenantization.md`
|
||||
- 部署坑手册:Phase 4 补 `docs/deploy-production.md`(尚未存在)
|
||||
- Phase 2 实施计划:`docs/superpowers/plans/2026-10-13-phase2-platform-layer.md`
|
||||
- 部署坑手册:Phase 4 补 `docs/deploy-production.md`(尚未存在;Phase 4 部署记档段已锚定要点)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# all-kefu
|
||||
|
||||
统一多租户 AI 客服平台:一套引擎 + 租户配置包,消除此前"每客户复制一套代码"的分叉模式。项目由 ai-kefu / ai-kefu-jc 两个复制分叉项目合并而来(jc 为主线),Phase 1 引擎合并与租户化改造已完成。
|
||||
统一多租户 AI 客服平台:一套引擎 + 租户配置包,消除此前"每客户复制一套代码"的分叉模式。项目由 ai-kefu / ai-kefu-jc 两个复制分叉项目合并而来(jc 为主线),Phase 1 引擎合并与租户化改造、Phase 2 平台层(platform.db + admin API)已完成。
|
||||
|
||||
**现状能力**(Phase 1 收尾时点):
|
||||
**现状能力**(Phase 2 收尾时点):
|
||||
|
||||
- 多租户引擎:TenantRuntime 隔离边界,engine 包不感知平台层,可整体拎走独立部署。
|
||||
- 站点模型:一租户多站点(品牌/主题/站点级覆盖),词表、提示词、品牌全部配置化(seed_profiles 出厂包 + 文件级 registry_reload 热加载,Phase 2 升级为 platform.db)。
|
||||
- 站点模型:一租户多站点(品牌/主题/站点级覆盖),词表、提示词、品牌全部配置化;配置存 platform.db 版本快照(分组校验 / 回滚 / 热加载),seed_profiles 为出厂档案与测试形态。
|
||||
- Host 路由:按域名解析租户,未知 Host 404;WS 通道租户化客户端 key 防串库,另有限流与每 IP 连接上限。
|
||||
- 引擎行为:cc 区号补全判定树、多语言墙(中/西/葡借道/俄/阿)、dual-contact 分步双要、CC 强制墙、lead 邮件推送。
|
||||
- 平台层(Phase 2):scrypt 认证 + 内存 token + 登录限流;租户管理(创建向导 / 站点 / 域名 / 停用);配置版本管线(整组替换 / 回滚 / 重置出厂 / 试金石);头像上传(魔数校验 + uuid 重命名);数据视图(会话 / 推送日志 / 删除,租户 scope 强制);统计(卡片 / 漏斗 / 趋势 / 分布 / 推送健康,逐库聚合禁 ATTACH);DB 源生产装配(TENANT_SOURCE=db 默认 + bootstrap_platform.py 引导)。admin API 全在 `/api/platform` 前缀(Host 豁免、路由依赖守护),端点清单见 [CLAUDE.md](CLAUDE.md)。
|
||||
|
||||
**本地跑法与工作约定**:见 [CLAUDE.md](CLAUDE.md)(含 uvicorn 启动命令、Host 访问方式、Windows PYTHONUTF8 要求、全量回归基线 459)。
|
||||
**本地跑法与工作约定**:见 [CLAUDE.md](CLAUDE.md)(含 uvicorn 启动命令、Host 访问方式、Windows PYTHONUTF8 要求、全量回归基线 566)。
|
||||
|
||||
设计文档(spec):[docs/superpowers/specs/2026-10-12-unified-kefu-platform-design.md](docs/superpowers/specs/2026-10-12-unified-kefu-platform-design.md)
|
||||
|
||||
Reference in New Issue
Block a user