2026-09-24 07:51:03 +08:00
|
|
|
|
# IM 群组 UI 插件投放 · 线上取证操作单
|
|
|
|
|
|
|
2026-10-10 23:13:22 +08:00
|
|
|
|
> **工作区**:`E:\ProgramData\AIProject\ai1net-dsh-server`|**线**:IM 线(第 14 棒)|**日期**:2026-09-23 20:2x
|
2026-09-24 07:51:03 +08:00
|
|
|
|
> **插件**:`@dsh-local/im-conversation-tabs` v`0.1.0`(E 单主交付,落点 = 实例内 dsh 会话页 tab)
|
|
|
|
|
|
> **投放物指纹**:tgz sha256 `c9b1184aca8eadd6c84316d2e79427546a65f59f0614a2ec9704c895a311bfb1`(21,494 B)
|
|
|
|
|
|
> |`lib/client.js` md5 `21fbc52fdedc9915238dd1c3f5c5e827`(= 本机源 / 包内 / 47 共享层三处逐字一致)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 一、服务端侧已完成(第 14 棒,⛔ 无需再做)
|
|
|
|
|
|
|
|
|
|
|
|
| 步骤 | 结果 | 证据 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| 打包(tgz,`package/` 布局) | ✅ | 包内 4 文件;三件齐(`exports['./client']` / `dsh.client.platform='web'` / `dsh.bundle.patch`)断言 rc=0 |
|
|
|
|
|
|
| admin 导入候选池 | ✅ http=200 | `replaced:false` · `trustedOverride:false` · `compat.level=unknown`(非 incompatible)· `blocked` 无 P0 ⇒ **未用信任放行**;审计 `audit_log` id **360** `upload_business_plugin` |
|
|
|
|
|
|
| 池内可见 | ✅ 4 → **5** 条 | `GET /api/plugins/business` 命中 `@dsh-local/im-conversation-tabs` v0.1.0 |
|
|
|
|
|
|
| 发布到共享层(`link:` 装配前提) | ✅ `action:"created"` | 目录 `/var/lib/dshs/bundled-plugins/_dsh-local_im-conversation-tabs`(root:root,全 world-readable);`fileRef = link:/var/lib/dshs/bundled-plugins/_dsh-local_im-conversation-tabs`;审计 id **361** `share_business_plugin` |
|
|
|
|
|
|
| 回滚素材 | ✅ | `/opt/dsh/backups/plugins/_dsh-local_im-conversation-tabs/0.1.0.tgz` |
|
|
|
|
|
|
| 临时 admin 会话 | ✅ 已删 | `deleted_sessions=1`;临时文件(payload / 响应 / 暂存 tgz)**零残留** |
|
|
|
|
|
|
|
|
|
|
|
|
⚠️ **为何必须做「发布到共享层」**:装配协议 = `link:`(2026-09-23 拍板)⇒ profile 里写的是软链依赖,目标即共享层目录。**只进候选池不发布**,用户点启用会撞 `plugin_bundle_missing`(`friendlyError` 把它翻成「该插件尚未发布到共享包库,请先联系管理员完成发布」)⇒ 半成品投放。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 二、待用户动作(3 步,约 2 分钟)
|
|
|
|
|
|
|
|
|
|
|
|
### 步 1 · 在实例内启用
|
|
|
|
|
|
|
|
|
|
|
|
实例 → **设置 → 功能管理** → 找到 `@dsh-local/im-conversation-tabs` → 打开开关。
|
|
|
|
|
|
|
|
|
|
|
|
- 该插件**未声明数据面**(`dataPlane.state = none`)⇒ 不会被「未建库不许开启」门禁拦。
|
|
|
|
|
|
- 点开后平台**自动**执行:写 profile(`link:` 引用)→ pnpm 建链 → **重启实例 + 探活**(无需另做重启)。
|
|
|
|
|
|
|
|
|
|
|
|
### 步 2 · 确认实例真的带上了包(只读,一条命令)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
ssh bt-server 'ls -la /var/lib/dshs/users/<你的 uid>/home/profiles/web/node_modules/@dsh-local/ | grep im-conversation-tabs'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
期望:一行**软链**,形如 `im-conversation-tabs -> …/bundled-plugins/_dsh-local_im-conversation-tabs`;退出码 `0`。
|
|
|
|
|
|
(uid 查法:`ssh bt-server 'ls /var/lib/dshs/users/'`,目录名 = 你的 userId。)
|
|
|
|
|
|
|
|
|
|
|
|
### 步 3 · 线上取证(浏览器,见第三节三条)
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 三、线上取证判据(生效链路第 ④ 关)
|
|
|
|
|
|
|
|
|
|
|
|
> 🔴 **口径更正(本棒实测)**:**IM 链路在服务端一条日志都不写** —— `src/im/ws.ts` / `src/web/routes/im.ts` / `src/supervisor/proxy.ts` 三处 `process.stderr` / `console.*` **零命中**;`reject()` 只回一帧 `error` 再 `destroy()`,**不落日志**。⇒ 判据必须在**客户端**取,⛔ `journalctl | grep bad-op` 恒空(空 ≠ 没发生)。
|
|
|
|
|
|
|
|
|
|
|
|
| # | 判据 | 命令 / 操作 | 期望(+退出码) |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| ① | 会话区被接管,tab 行出现 | 浏览器 DevTools Console:`document.querySelectorAll('.imtabs-bar').length` | `1` |
|
|
|
|
|
|
| ② | tab 行内容(dsh 会话 + 群组 tab) | `document.querySelectorAll('.imtabs-tab').length` | `>= 1`(首屏 = dsh 会话 tab;开群组 tab 后递增) |
|
|
|
|
|
|
| ③ | 🔴 协议对齐(原 P0 不复现) | DevTools → **Network → WS → `/api/im/ws` → Messages**:看帧流水 | 出站只有 `{"op":"subscribe"…}` / `{"op":"ping"}` / `{"op":"resume"…}`;入站首帧 `{"op":"hello"…}`;**不出现** `{"op":"error","error":"bad-op"…}` |
|
|
|
|
|
|
| ④ | 连接态健康 | `document.querySelector('.imtabs-conn').className` | 不含 `imtabs-connBad`(含 `connWarn` 视为未通过) |
|
|
|
|
|
|
| ⑤ | 槽位命中(失效时可见) | `document.querySelector('.imtabs-alert')` | `null`(**非 null ⇒ 官方槽名又变了**,见 `.imtabs-alert` 提示文案) |
|
|
|
|
|
|
| ⑥ | 房间消息真的渲染 | 在房间里发一条消息 → 群组 tab 内消息流 | 消息出现在列表中(游标补齐走 REST `pull` + WS `resume`,两侧按 `m.id` 去重) |
|
|
|
|
|
|
|
|
|
|
|
|
**判定口径**:①②③④⑤⑥ 全过 ⇒ E 单「线上可见面」通过,**E 单可判「已交付」**;任一条不过 ⇒ 本项记「未通过」,把 ③ 的帧流水原文回传(那是定位 P0 类的唯一凭据)。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 四、前置条件与两个已知缺口
|
|
|
|
|
|
|
|
|
|
|
|
1. 🔴 **实例在哪台机决定成败**:
|
|
|
|
|
|
- 实例在 **47(Manager)** ⇒ 共享层已就绪,**直接可用** ✅(47 现有实例 = `admin`,uid `114801`)。
|
|
|
|
|
|
⚠️ 47 上该实例的 bwrap 当前**没有** `--ro-bind-try /var/lib/dshs/bundled-plugins`(启动于 09-21 00:55,早于该绑定上线)⇒ **必须让它走一次重启**(步 1 的启用动作自带重启,即已满足)。
|
|
|
|
|
|
- 实例在 **106(worker)** ⇒ **共享层缺该包**(106 只有 `_dsh-local_storyforge` / `dsh-plugin-mcn-suite`)⇒ 启用会失败。**已知缺口**:跨节点内容分发**无平台侧通路**(投放线 §5-2 记为「需单独立项」)。临时办法(手工同步,沿用第 8 棒做法):
|
|
|
|
|
|
```bash
|
|
|
|
|
|
ssh bt-server 'tar -C /var/lib/dshs/bundled-plugins -czf - _dsh-local_im-conversation-tabs' \
|
|
|
|
|
|
| ssh test106 'tar -C /var/lib/dshs/bundled-plugins -xzf - && chown -R root:root /var/lib/dshs/bundled-plugins/_dsh-local_im-conversation-tabs'
|
|
|
|
|
|
ssh bt-server 'cat /var/lib/dshs/bundled-plugins/.manifest.json' | ssh test106 'cat > /var/lib/dshs/bundled-plugins/.manifest.json'
|
|
|
|
|
|
```
|
|
|
|
|
|
(第二条会整体覆盖 106 的 manifest ⇒ 执行前先比对两边内容,只差新增那一条才可覆盖。)
|
|
|
|
|
|
2. ⚠️ **admin 实例 profile 里有一条陈旧软链**(`node_modules/@dsh-local/storyforge`,09-21 建立,但 `dependencies` / `bundles` 里都已无该项)—— 第 8 棒探针残留。**本轮只报告未动**(R7)。与本次取证无关。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 五、回滚(任一步出问题)
|
|
|
|
|
|
|
|
|
|
|
|
| 目的 | 动作 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| 只关插件(保留投放) | 实例 → 设置 → 功能管理 → 关掉开关(平台自动 `pnpm remove` + 重启探活) |
|
|
|
|
|
|
| 撤出用户面(停止发布) | `POST /api/plugins/business/share` 的逆操作:`DELETE /api/plugins/business/:id/share`(⛔ 若仍有用户启用会被 409 挡下 —— 这是设计) |
|
|
|
|
|
|
| 彻底下架 | `DELETE /api/plugins/business/%40dsh-local%2Fim-conversation-tabs`(清池行 + tgz) |
|
|
|
|
|
|
| 换回旧包 | 回滚素材 `/opt/dsh/backups/plugins/_dsh-local_im-conversation-tabs/0.1.0.tgz` |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 六、本单未做 / 未验(⛔ 不假装做了)
|
|
|
|
|
|
|
|
|
|
|
|
1. ⛔ **未替用户点启用** —— 「启用」是用户动作(投放语义 = 用户自助开通),本单只把**可自动部分**做完。
|
|
|
|
|
|
2. ⛔ **未做网关部署**(选型单 §十五 的 gateway 侧部署)|⛔ **未接在线态取数**(`stats().presenceIngest='not-wired'`)—— 两项与本次投放解耦,排在再后。
|
|
|
|
|
|
3. ⛔ **未同步 106 共享层** —— 见 §四-1,属「跨节点内容分发」立项面;本单给出命令但**未执行**(避免无谓的多机生产改动)。
|
|
|
|
|
|
4. ⛔ 未 commit / push / scp 源码 | ⛔ 未改 `poc/im-conversation-tabs/**` 与 `src/im/**` 任何一行 | ⛔ 未引新依赖 | ⛔ 未铺 profile。
|