diff --git a/docs/deploy-wangliang.md b/docs/deploy-wangliang.md new file mode 100644 index 0000000..ac53152 --- /dev/null +++ b/docs/deploy-wangliang.md @@ -0,0 +1,171 @@ +# crm-ai 在 wangliang 工作区的部署记录 + +> 部署时间:2026-10-23 | 部署位置:`/data/dev/wangliang/crm-ai` | 代码版本:`main @ 1ae25f1` +> +> 本文件记录这套栈**实际跑成什么样**、和仓库默认配置差在哪、怎么运维、以及还缺哪些凭据。 +> 环境相关的临时信息写在这里,通用约定见 `/data/dev/_shared/NOTES.md`。 + +## 1. 交付摘要 + +一套自包含的客服 + CRM 栈(与仓库 `docker-compose.yml` 一致,Compose 项目名 `crm-new`): + +| 组件 | 容器 | 宿主端口 | 状态 | +|---|---|---|---| +| LibreDesk v2.9.0-rc.10(客服工作台 + 内置 AI) | `crmnew-libredesk` | `9000` | ✅ healthy | +| Twenty CRM v2.30.1(server) | `crmnew-twenty-server` | `3100` | ✅ healthy | +| Twenty worker | `crmnew-twenty-worker` | — | ✅ running | +| bridge(业务工具 / RPC 集成) | `crmnew-bridge` | `4100` | ✅ healthy | +| PostgreSQL 18 + pgvector | `crmnew-postgres` | `127.0.0.1:5440` | ✅ healthy | +| Redis 8.8 | `crmnew-redis` | `127.0.0.1:6390` | ✅ healthy | +| IMAP ID 注入代理(163 专用) | `crmnew-imap-proxy` | `127.0.0.1:1993` | ✅ running | + +数据卷:`crm-new_pg-data`、`crm-new_redis-data`、`crm-new_twenty-data`(`docker compose down` 不会删)。 + +## 2. 访问地址与账号 + +| 入口 | 地址 | 账号 | +|---|---|---| +| LibreDesk(工作台 / 帮助中心 / widget) | https://wangliang-crm.dev.gaia888.com | `System` /密码见下方命令 | +| Twenty CRM | https://wangliang-twenty.dev.gaia888.com | `admin@yehwang.com` /密码见下方命令 | +| bridge(调试/集成) | https://wangliang-crmbridge.dev.gaia888.com | 请求头 `X-Tools-Key`,值见下方命令 | + +```bash +cd /data/dev/wangliang/crm-ai +grep '^LIBREDESK_SYSTEM_USER_PASSWORD=' .env # LibreDesk System 密码 +grep '^TWENTY_ADMIN_PASSWORD=' .env # Twenty 管理员密码 +grep '^TOOLS_SHARED_KEY=' .env # bridge 工具密钥 +``` + +> 密钥只存在 `.env`(600)与 `libredesk/config/config.toml`(600),两者都已被 `.gitignore` 排除,**不要提交**。 + +## 3. 与仓库默认配置的差异(重要) + +| 项 | 仓库默认 | 本环境 | 原因 | +|---|---|---|---| +| `LIBREDESK_IMAGE` | `libredesk/libredesk:v2.9.0-rc.10` | `libredesk/libredesk:v2.9.0-rc.10-**arm64**` | 宿主是 ARM64,`v2.9.0-rc.10` 只有 amd64;官方另发了 `-arm64` 同版本 tag,实测原生 `linux/arm64` 运行 | +| `LIBREDESK_BASE_URL` | `http://localhost:9000` | `https://wangliang-crm.dev.gaia888.com` | 走宿主 nginx 的 HTTPS | +| `TWENTY_SERVER_URL` | `http://192.168.31.61:3100` | `https://wangliang-twenty.dev.gaia888.com` | 前端 `REACT_APP_SERVER_BASE_URL` 用的就是它(已核对页面内联配置) | +| `RECOMMEND_URL` | 测试服地址 | `jsonrpc://host.docker.internal:24503` | 用 **本工作区** 的 product-service(宿主 socat 24503 → 容器 9503) | +| `ORDER_API_URL` | 测试服地址 | `jsonrpc://host.docker.internal:24505` | trading-service(24505 → 9505) | +| `INVENTORY_API_URL` | 测试服地址 | `jsonrpc://host.docker.internal:24509` | eta-service(24509 → 9509) | +| `MEMBER_RPC_URL` | 测试服地址 | `jsonrpc://host.docker.internal:24506` | member-service(24506 → 9506) | +| `IDENTITY_DEV_MODE` | `false` | `true` | dev 环境方便 widget 联调;**上生产前改回 `false`** | +| `public-gateway` / `cloudflared` | compose 里默认启动 | **未启动** | 已有宿主 nginx 直出 HTTPS,不需要 Cloudflare 临时隧道 | + +`host.docker.internal` 由 compose 的 `extra_hosts` 注入,指向宿主;`24503-24509` 是本工作区的 RPC 宿主转发端口 +(见 `conf/dev/rpc-forward.sh`,cron 每分钟自愈)。 + +## 4. 已完成初始化 + +### LibreDesk +- `System` 账号可登录(浏览器实测落到 `/inboxes/assigned`)。 +- `app.root_url = https://wangliang-crm.dev.gaia888.com`(`scripts/set-public-url.js`)。 +- 10 个 bridge 工具已注册(`scripts/register-tools.js`):`search_products / search_orders / search_inventory / + search_kb / search_email_history / collect_info / send_verification_code / verify_email_code / handoff_to_human / + create_opportunity`。 +- 默认助理 `Yehwang Assistant`(#1,10 个工具、引用与 handoff 开启)。 +- Live chat Inbox `Website`(#1,uuid `ad25d7a4-722d-4561-aebf-9d8f60952d09`)。 +- Automation:新会话自动分配给助理(#1,enabled)。 + +### Twenty +- Workspace `Yehwang`:`2ff5a56b-95d8-40e1-85cf-529ad3a70ed7`(`ACTIVE`)。 +- API key 已生成并写入 `.env` 的 `TWENTY_API_KEY`;GraphQL `companies` 查询验证通过。 +- 浏览器实测可登录,首次进入 `/install-apps`(Twenty 的新工作区引导页,正常现象)。 + +### bridge +- `/healthz` 正常,10 个工具就绪。 +- 实测 `search_products` 直接命中**本工作区真实商品数据**(如 `Stainless Steel Y-Chain Necklace` / `$6.73` / + `product_id=11079`),说明 `host.docker.internal:24503` 的 JSONRPC 链路是通的。 +- `search_orders` 未验证身份时按设计返回 `verification_required`。 + +## 5. 运维命令 + +```bash +cd /data/dev/wangliang/crm-ai + +docker compose ps # 状态 +docker compose logs -f --tail=100 libredesk +docker compose logs -f --tail=100 twenty-server +docker compose logs -f --tail=100 bridge + +docker compose start # 启动已有容器 +docker compose stop # 停止(保留数据) +docker compose restart libredesk # 重启单个服务 + +# 改 .env 后(bridge 必做;libredesk 也要重建才会读到新 env) +docker compose up -d --force-recreate bridge +docker compose up -d --force-recreate libredesk + +# 改 LibreDesk 配置模板后 +node scripts/render-config.js && docker compose up -d --force-recreate libredesk + +# 备份(DB + Twenty 自带数据都在 PG 里;卷里是上传文件) +mkdir -p backups +docker exec crmnew-postgres pg_dump -U crmnew -Fc libredesk > backups/libredesk-$(date +%F).dump +docker exec crmnew-postgres pg_dump -U crmnew -Fc twenty > backups/twenty-$(date +%F).dump +docker exec crmnew-postgres pg_dump -U crmnew -Fc bridge > backups/bridge-$(date +%F).dump + +# 恢复 +docker exec -i crmnew-postgres pg_restore -U crmnew -d libredesk --clean --if-exists < backups/libredesk-YYYY-MM-DD.dump +``` + +所有容器都是 `restart: unless-stopped`,Docker 守护进程重启后会自己拉起来。 + +## 6. 域名反向代理 + +宿主 nginx 新增了**独立**配置文件 `/etc/nginx/conf.d/zz-wangliang-crm.conf`: + +- `wangliang-crm.dev.gaia888.com` → `127.0.0.1:9000` +- `wangliang-twenty.dev.gaia888.com` → `127.0.0.1:3100` +- `wangliang-crmbridge.dev.gaia888.com` → `127.0.0.1:4100` +- 80 → 308 跳 HTTPS;证书复用 `*.dev.gaia888.com`(通配一级域名,三个域名都被覆盖)。 + +它**没有改** `devstack.conf` 的 map/正则,因此不影响其它工作区。 + +改完后的自检 / 重载(query 用户没有 root,用容器代跑): + +```bash +docker run --rm --privileged --pid=host redis:7-alpine \ + sh -c 'nsenter -t 1 -m -u -i -n -p /usr/sbin/nginx -t' +docker run --rm --privileged --pid=host redis:7-alpine \ + sh -c 'nsenter -t 1 -m -u -i -n -p /usr/sbin/nginx -s reload' +``` + +回滚(只需删掉这个文件并 reload,其它站点不受影响): + +```bash +docker run --rm -v /etc/nginx/conf.d:/c redis:7-alpine rm -f /c/zz-wangliang-crm.conf +# 再执行上面的 nginx -t / -s reload +``` + +## 7. 尚未配置(需要真实凭据,不是代码问题) + +| 项 | 现状 | 补法 | +|---|---|---| +| **AI Provider(DashScope)** | `LLM_*` / `EMB_*` 为空,助理不会真正调模型 | `DASHSCOPE_API_KEY=sk-xxx node scripts/configure-ai.js`(需能访问 dashscope) | +| **SMTP / 163 邮件** | `SMTP_*` 为空,OTP 与邮件渠道不可用 | 填 `.env` 后 `node scripts/configure-email.js` + `node scripts/add-email-inbox.js` | +| **知识库 RAG(search_kb)** | 无 `RAG_PG_URL`,工具返回 503 | 指向旧 KB 库后运行 `scripts/import-kb-helpcenter.js` / `rebuild-kb-index.sh` | +| **WhatsApp 渠道** | 无 Meta 凭据 | 填 `WA_*` 后 `scripts/add-whatsapp-inbox.js`;需要公网回调(本栈已有 HTTPS,可不用 cloudflared) | +| **public-gateway / cloudflared** | 未启动 | 需要 Cloudflare 临时隧道时再 `docker compose up -d public-gateway cloudflared` | + +> `IDENTITY_DEV_MODE=true` 只适合 dev;上生产前改回 `false`。 + +## 8. 验证记录(2026-10-23) + +```text +$ curl -o /dev/null -w '%{http_code}' https://wangliang-crm.dev.gaia888.com/health # 200 +$ curl -o /dev/null -w '%{http_code}' https://wangliang-twenty.dev.gaia888.com/healthz # 200 +$ curl -o /dev/null -w '%{http_code}' https://wangliang-crmbridge.dev.gaia888.com/healthz # 200 +$ curl -s https://wangliang-crmbridge.dev.gaia888.com/healthz + {"ok":true,"service":"crm-tools","tools":[10 个],"sites":["www","us","eu"]} +$ curl -s -X POST http://127.0.0.1:4100/tools/search_products -H 'X-Tools-Key: …' -d '{"query":"stainless steel necklace"}' + {"ok":true,"products":[{"name":"Stainless Steel Y-Chain Necklace","price":"$6.73","product_id":11079}]} +``` + +浏览器(Playwright 真 Chrome,1440×900): + +- LibreDesk 登录成功 → `/inboxes/assigned`(截图 `docs/screenshots/wangliang-libredesk-inbox.png`) +- Twenty 登录成功 → `/install-apps` 引导页(截图 `docs/screenshots/wangliang-twenty-onboarding.png`) +- 回归:`wangliang-www` 仍 200,`wangliang-agent` 仍 302(宿主 nginx 未被改坏) + +资源占用:约 1.9 GB 内存(Twenty worker 约 1.0 GB + server 0.6 GB + PG/Redis/bridge/LibreDesk 约 0.3 GB)。 diff --git a/docs/screenshots/wangliang-libredesk-inbox.png b/docs/screenshots/wangliang-libredesk-inbox.png new file mode 100644 index 0000000..eb77c98 Binary files /dev/null and b/docs/screenshots/wangliang-libredesk-inbox.png differ diff --git a/docs/screenshots/wangliang-twenty-onboarding.png b/docs/screenshots/wangliang-twenty-onboarding.png new file mode 100644 index 0000000..7447a76 Binary files /dev/null and b/docs/screenshots/wangliang-twenty-onboarding.png differ