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

95 lines
8.2 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(SPA history 路由);`/admin` 前缀**不经过租户中间件解析**(豁免规则扩 `/admin`——注意精确:`/admin` 与 `/admin/`,防 `/admin-evil`);dist 不存在时(本地测试环境)`/admin/` 返回 503/404 带说明(开发用 pnpm dev,不炸整个应用)
- [ ] 实现:
- main.py:`ADMIN_DIST = BASE_DIR / "admin" / "dist"`;存在则挂 StaticFiles(directory=dist, html=True) 到 `/admin`——**SPA fallback**:Starlette 静态 html=True 不做 history fallback,需自定义 404 handler 限定 `/admin/*` 非 api 路径回 index.html(读 dist 结构确认 assets 子目录)
- host_middleware 豁免扩 `/admin`(精确 startswith("/admin/") or == "/admin"——防误豁免 `/adminX`)
- Dockerfile:阶段一 node:20-alpine(WORKDIR /build、拷 admin/、corepack enable pnpm、pnpm install --frozen-lockfile、pnpm build);阶段二 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 curl /health
- .dockerignore:.venv、node_modules、dist、.worktrees、tests、docs、data、tenants、platform.db*、.git
- [ ] 验收:后端全量(595+新增)+ admin pnpm build 后本地起 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`
- [ ] 失败测试(用两个老项目的真实 db 副贝造夹具——拷到 tmp):
- 导入 jc 库 → 租户库(bootstrap 后的 jiancai)行数一致(五表对账);**conversation_id 原样**(external_key 连续性);session_id/page_token/status 原样;pending 推送日志 → failed 加注;**幂等**(跑两遍第二遍零变化)
- auto 库同理
- [ ] 实现:读老库(aiosqlite 只读)逐表 SELECT → 按目标租户库 INSERT OR IGNORE(幂等键:conversation_id/表主键);对账报告(每表 行数源/目标/跳过);`--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 拷贝 → import_legacy.py → 对账核对
5. 切换 runbook(独立节):验证清单(不切域名全功能验证)→ 切反代 → 观察一周(breach_rate、推送成功率、磁盘)→ 老项目停用归档
6. 日常运维:备份(platform.db + tenants/ 用 SQLite backup API,**禁运行时直拷**;季度恢复演练)、日志(封顶已配)、归档(archive.py 用法)、升级发版(镜像 tag 回滚 30 秒)
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(config_views 六个 resolver 读 env 前先查 platform_settings——**功能闭环**,测试覆盖:设置 ai.api_key 后租户空槽取它)
- 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 接线若超半日可单独挂回清单——但它是平台设置页的功能闭环,优先保)。