Files
ai-kefu-admin/docs/superpowers/plans/2026-10-14-phase4-deploy-migration.md
T

96 lines
9.6 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.
# Phase 4:部署与迁移实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 交付上线全部工件:Docker 两阶段构建与生产 compose、admin dist 的 FastAPI 服务、存量数据导入脚本、数据归档脚本、部署手册(继承 jc 血泪)、切换 runbook;顺手清 Phase 4 清理单中部署相关项。
**Architecture:** 单容器(python:3.13-slim + admin dist 拷入);FastAPI 在 `/admin/` 前缀服务 SPA(history fallback 限定该前缀)——反代层可走"独立管理域名指到同容器"或"路径分流"两形态(spec §7.1 同源零 CORS);存量导入用 scripts/import_legacy.py(保 conversation_id/session/page_token/status,幂等,对账报告)。
**Tech Stack:** Docker multi-stage(node:20-alpine 构建 admin → python:3.13-slim);其余同现状。
**参照 spec:** §8(数据迁移)、§9(部署全部条目——逐项对照落实);CLAUDE.md「Phase 4 部署记档」与「Phase 4 清理单」9 项。
**重要边界:本阶段交付部署工件(代码/脚本/文档/配置模板),不执行实际部署。** 实际部署到阿里云时按用户全局 SOP 另行确认流程(全局 CLAUDE.md 规程)。
---
### Task 1: admin 生产服务与 Docker 两阶段构建
**Files:**
- Modify: `app/main.py`(挂 /admin/ 静态 + SPA fallback)、`Dockerfile`(新建两阶段)、`docker-compose.yml`(新建)、`.dockerignore`(新建)
- Test: `tests/test_admin_serving.py`
- [ ] 失败测试:
- `GET /admin/` 返回 index.html(dist 存在时);`GET /admin/assets/xxx` 静态可达;未知路径 `/admin/xxx` fallback 返回 index.html(**加固项**:admin 实为 hash 路由非必需,但深链/刷新兜底;**fallback handler 必须只对 /admin 前缀生效,非 /admin 的 404 保持默认 JSON**——防 API 404 被 SPA 吞);`/admin` 前缀**不经过租户中间件解析**(豁免规则扩 `/admin`——精确 `== "/admin" or startswith("/admin/")`,防 `/admin-evil`);dist 不存在时(本地测试环境)`/admin/` 返回 503/404 带说明(开发用 pnpm dev,不炸整个应用)
- **/health 分级(spec §9 明文)**:进程状态 + 各租户 engine 状态 + 域名表版本;单租户库损坏 → 200 带 per-tenant degraded(不整体 500);registry.runtimes() 逐租户探库(轻查询,如 `SELECT 1`)
- [ ] 实现:
- main.py:`ADMIN_DIST = BASE_DIR / "admin" / "dist"`;存在则挂 StaticFiles(directory=dist, html=True) 到 `/admin`——SPA fallback:自定义 404 handler 限定 `/admin/*` 回 index.html(读 dist 结构确认 assets 子目录)
- host_middleware 豁免扩 `/admin`;`/health` 升级为分级响应(上述测试钉死)
- Dockerfile:阶段一 node:20-alpine(WORKDIR /build、拷 admin/、corepack enable + **package.json 的 packageManager 字段钉 pnpm 版本**、pnpm install --frozen-lockfile、`VITE_BASE_URL=/admin/ pnpm build`——**资源前缀必须 /admin/ 否则挂前缀后白屏**);阶段二 python:3.13-slim(拷 app/ static/ seed_profiles/ scripts/ requirements、pip install、COPY --from=build /build/admin/dist admin/dist);CMD uvicorn --workers 1
- docker-compose.yml:`name: all-kefu`(互删教训);单 app 服务;端口 8018:8018;env_file .env;volume `./data:/app/data` + `./tenants:/app/tenants`;TZ=Asia/Shanghai;mem_limit 768m;logging json-file max-size 10m max-file 3;healthcheck **CMD-SHELL 用 python urllib(slim 无 curl)**打 /health
- .dockerignore:.venv、**`**/node_modules`、`admin/dist`**(裸 node_modules/dist 只匹配根)、.worktrees、tests、docs、data、tenants、platform.db*、.git
- [ ] 验收:后端全量(595+新增)+ admin `VITE_BASE_URL=/admin/` 构建后本地起 FastAPI 验证 /admin/ 服务与资源路径(冒烟)+ `docker compose build` 成功(本机 Docker 可用则构建验证,不可用则文档注明服务器构建)
- [ ] 提交 `feat: admin 生产服务(/admin SPA fallback) + Docker两阶段构建 + 生产compose`
### Task 2: 存量导入脚本 import_legacy.py
**Files:**
- Create: `scripts/import_legacy.py`、`tests/test_import_legacy.py`
- [ ] 失败测试(老库路径经 env 指路(如 ALL_KEFU_TEST_LEGACY_JC/AUTO),**缺失则 skipif**——机器相关且含真实数据;测试只拷结构+少量行到 tmp 或造脱敏小库):
- 导入 jc 库 → 租户库(bootstrap 后的 jiancai)行数一致(五表对账 + **最近 10 条会话抽查**【spec §8 原文】);**conversation_id 原样**(external_key 连续性);session_id/page_token/status 原样;pending 推送日志 → failed 加注;**幂等**(跑两遍第二遍零变化)
- auto 库同理
- [ ] 实现:读老库(aiosqlite 只读)逐表 SELECT → 按目标租户库 INSERT OR IGNORE(幂等键:conversation_id/表主键);对账报告(每表 行数源/目标/跳过 + 抽查 10 条摘要);`--source <db路径> --tenant <slug>` CLI;README 用法段
- [ ] 提交 `feat: 存量导入脚本(保ID/session状态、pending→failed、幂等、对账)`
### Task 3: 数据归档脚本 archive.py
**Files:**
- Create: `scripts/archive.py`、`tests/test_archive.py`
- [ ] 失败测试:造 >N 天会话 → 归档(导出 JSON 到 data/archives/)→ 清库 → VACUUM → 库文件变小;<N 天不动;--dry-run 只列不动
- [ ] 实现:`--tenant slug --days N(默认 365) --dry-run`;删除序(lead_push_logs→evaluations→profiles→messages→conversations,倒 FK 序);导出含完整行;VACUUM;报告
- [ ] 提交 `feat: 数据归档脚本(导出/清理/VACUUM/dry-run)`
### Task 4: 部署手册与 openresty 模板
**Files:**
- Create: `docs/deploy-production.md`、`deploy/openresty-all-kefu.conf.template`
- [ ] deploy-production.md 章节(**继承 jc 血泪手册全部条目** + CLAUDE.md Phase 4 记档落位):
1. 环境与前置(服务器、Python/Docker、**pip install -r requirements.txt 含 python-multipart**、**ADMIN_PASSWORD 必设**(默认值启动警告)、TENANT_SOURCE=db)
2. 首次部署流程:clone → .env(逐项说明)→ bootstrap_platform.py(超管引导+两租户导入)→ docker compose up → 健康检查
3. 反代配置(1Panel):租户域名→127.0.0.1:8018;**admin 两形态**(独立域名 或 路径分流到 /admin);WS 单套升级头;/static/widget.js no-cache;**/api/public 放行**(头像);主 conf include 行检查;nginx -t 后 reload
4. 存量迁移:老项目 db 拷贝纪律(**停老容器或 SQLite backup API/VACUUM INTO 导出,禁运行时裸 cp**)→ scp 到新机 → import_legacy.py → 对账核对
5. 切换 runbook(独立节):验证清单(不切域名全功能验证)→ 切反代 → 观察一周(breach_rate、推送成功率、磁盘)→ 老项目停用归档
6. 日常运维:备份(platform.db + tenants/ 用 SQLite backup API,**禁运行时直拷**;季度恢复演练)、日志(封顶已配;**PII 对齐**:引擎日志现存会话内容摘录,与 spec §9"不打聊天内容"冲突——手册如实记当前状态并在观察期收口)、归档(archive.py 用法)、升级发版(build 后打版本 tag、compose image: 引用 tag,回滚=改 tag up 30 秒)、**加租户=反代加一条不起容器**、bootstrap 执行位置(`docker compose run --rm app python scripts/bootstrap_platform.py`)
7. 踩坑实录(继承 jc:compose 撞名互删、WS 426、widget 缓存、1Panel include、bottom-left 溢出 + 本项目新增:mem_limit、TZ、/api/public 404)
- [ ] openresty 模板:完整可抄配置(upstream + 租户 server 块 + admin server 块两形态注释 + WS + 缓存策略)
- [ ] 提交 `docs: 部署手册(继承jc血泪+切换runbook+备份演练)+ openresty模板`
### Task 5: 清理单部署相关批次
**Files:**(按清理单逐项)
- resolver 回退链接入 platform_settings——**架构方向(同步 resolver 无法读异步 DB)**:lifespan 启动把 platform_settings 快照进内存 dict + PUT /settings 提交后刷新快照(workers=1 下与内存 token 表同模式);回退链 = 租户槽位 → 快照 → env;测试覆盖:设置 ai.api_key 后租户空槽取它、PUT 后即时生效、快照缺失回退 env
- email/test 与 push/test 限流(复用 LoginLimiter 模式,5 次/15 分钟每租户)
- ECharts 收窄(plugins/echarts.ts 只注册 Bar/Line/Pie + 现用组件)+ 租户切换器 useTenantOptions composable 收拢三页(conversations/config/stats)
- 锁屏/通知组件代码删除(enabled:false 已不可达——删组件文件与配置项,grep 确认零引用)
- [ ] 每项独立提交;全量回归(后端+vitest+build)
- [ ] 提交(多项分别提交)
### Task 6: Phase 4 收尾
- [ ] 全量回归(根+tests/ 双跑;vitest;build;docker compose build 复验)
- [ ] CLAUDE.md/README 终态更新(项目完成状态、清理单结项核对、上线前检查清单浓缩版)
- [ ] 提交 `chore: Phase 4 收尾——部署工件齐备`
---
## 显式不做(Phase 4 范围外)
实际部署执行(按全局 SOP 另行确认);真实域名 DNS 变更;UniService 侧配置;CI/CD 流水线;监控告警系统(推送失败告警二期)。
## 执行注意
部署工件全部本地可验证(构建/测试/文档);涉及真实服务器的步骤只写进 runbook 不执行。清理单 Task 5 允许按耗时裁剪(resolver 接线若超半日可单独挂回清单——但它是平台设置页的功能闭环,优先保)。