Files
dsh_shenxian/dsh-server-docs/04-调整方案/59-重连反馈-实例启动加载动画.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
   保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
   工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
   必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
   + ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
   ⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
   验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

95 lines
6.0 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.
# 59 · 重连过程的可见反馈:实例启动中的加载动画
- 日期:2026-09-12
- 状态:✅ **已实施并验证**(服务器已生效;本地待提交)
- 触发:用户报「admin 会话出现连接异常,点击重连成功了,但**过程中没有加载/重连或重启进程的加载动画**」
- 关联:档案 49(导航过渡页 `wake.html`)、50(401 自愈注入 `__dshRecover`)、51(401 透明重放)、56(实例助手 `__dshAssist`)、58(本次重启的起因:实例内存优化)
---
## 一、现象与根因
用户遇到的"连接异常"是**平台重启导致实例被 drain**(档案 58 的内存优化需要重启 `dshs`)。**重连本身成功了**,问题在于**过程无反馈**。
排查后发现平台对"实例未就绪"的处理**分两条路径,只有一条有动画**:
| 路径 | 触发场景 | 现有处理 | 反馈 |
|---|---|---|---|
| **浏览器导航**(刷新/首次进入) | `GET` + `Accept: text/html` | 302 → `/wake.html?next=…`(自带旋转 + 进度条 + 步骤文案) | ✅ **有动画** |
| **XHR / fetch**(**页面已打开**时点重连) | `/api/*` | proxy **阻塞等待实例拉起**(`launch` + `waitForLaunchTokenForUser`,最长 **20 秒**)后继续转发 | ❌ **无任何反馈** |
`proxy.ts` 的注释写着这套设计的前提假设:
> 非导航(XHR / API)——**这是"页面已打开、再对话没反应"的主场景**。正解不是"回一个错误/动画",而是**等实例就绪后继续转发**:请求最终成功,**dsh 前端自己会保持它的 loading 态** → 用户无感,不需要刷新。
**假设不成立**:dsh 客户端在"连接异常 → 重连"这条路径上**并不显示等待态**。于是用户看到的是"点了重连,界面毫无动静,几十秒后突然好了"。
---
## 二、修复(纯客户端增强,不动协议)
在注入脚本里给**挂起的 API 请求**加计时器:超过阈值就亮出覆盖层,请求结束(无论成败)立刻撤掉。
| 设计点 | 取值 / 做法 | 理由 |
|---|---|---|
| 阈值 `SLOW_MS` | **3000 ms** | 正常 API 请求 < 1 s;超 3 s 基本只可能是服务端在等实例。低于此值**完全无感**,不打扰正常使用 |
| 覆盖层文案 | 「实例正在启动,请稍候…(已等待 N 秒)」 | 带秒数让用户知道"在推进"而不是卡死 |
| 与 401 自愈共存 | 同一个覆盖层元素,`state` 分 `pending` / `expired` | `expired`(401,随后整页 reload)是终态,不被 `pending` 覆盖、也不主动撤除 |
| **不误伤流式接口** | 只在 fetch promise **未 resolve** 时计时 | SSE / ReadableStream 在**响应头到达**时 promise 已 resolve → 计时已清 → **不会误触发**。这正是本方案安全的关键 |
| **spinner 只建一次** | 首次创建 DOM 后仅改文案节点的 `textContent` | 若每次重设 `innerHTML`,元素会被重建、`animation` 被打断 → 看起来像"转圈卡住不动" |
| 覆盖范围 | 仍只对 `/api/` 请求生效 | 与原有 401 逻辑一致,避免影响静态资源 |
| 失败安全 | 全部逻辑包在 try/catch 语义内,且 `if (window.__dshRecover) return` 幂等 | 注入脚本绝不能把页面弄坏 |
**顺带把这段脚本从"1516 字符单行双引号字符串"改成多行模板字符串**,并加注释说明两个用途 —— 这段代码后续还会改,单行形式已无法维护。
---
## 三、验证
| 项 | 结果 |
|---|---|
| 构建 | `npm run build`(tsc)退出码 **0** —— 证明模板字符串改写无语法错(含反引号 / `${` 的检查) |
| 重启 | drain 实例 → `systemctl restart dshs` → active、门户 200 |
| 注入生效 | 实例页 HTML 实测含 `__dshRecoverMsg`×2、`showPending`×2、`实例正在启动`×1、`已等待`×2、`var SLOW_MS = 3000` ✓ |
| 原有功能未丢 | `__dshRecover`×7、`__dshAssist`×6 ✓ |
| 文本片段 | 「实例正在启动,请稍候…」与「会话已过期(实例被回收重建)」两段文案都在 ✓ |
### 验证过程中的三个**假阴性**(都值得记)
取实例页 HTML 做断言时连着踩了三次坑,每次看起来都像"注入坏了":
1. **漏了 `Accept: text/html`** → 门户**只在客户端接受 HTML 时**才把上游 `accept-encoding` 覆盖为 `identity`(否则注入必然被 gzip 挡掉)→ 不带头就永远注入不上。日志里的 `[inject-recovery] skip: content-encoding=gzip` 是**判断这个的直接线索**。
2. **漏了 cookie jar** → `?token=` 会 303 到 `/` 并下发 `dsh-auth-*`;不用 `-c/-b jar` 跟随就拿不到最终页面(`size=0`)。此坑档案 56 已记过一次,**又踩了**。
3. **忘了 curl 不解压** → 响应被边缘 nginx 压缩,`size=8684` 就是 24.8 KB gzip 后的大小;grep 的是压缩字节流,自然一个关键字都搜不到。加 **`--compressed`** 即解。
> 结论:**验证实例页 HTML 的标准姿势**(三条缺一不可):
> ```bash
> curl -s -L --compressed -c jar -b jar -b "sid=$TOKEN" \
> -H "Accept: text/html,application/xhtml+xml" https://admin.alotbuy.com/
> ```
**未做的验证**:真实浏览器里"点重连 → 看到动画"的端到端观感(需用户在实际断连时确认)。逻辑链路已全部验证;阈值 3 s 与实际等待时长(最长 20 s)的关系也已在代码里对齐。
---
## 四、顺带发现:`web/wake.html` 双端不一致
| 位置 | 大小 | mtime |
|---|---|---|
| 本机 `dsh_shenxian/web/wake.html` | **4708** B | 09-11 21:26 |
| 服务器 `/opt/dshs/web/wake.html` | **4591** B | 09-11 18:11 |
本机版本更新但没有推到服务器 → **过渡页的实际表现与仓库记录不一致**。本次未处理(不属于当前任务,且疑似另一会话的在途改动),**列入待办:核对后同步**。
---
## 五、回滚
```bash
cp /opt/dsh/backups/proxy.ts.bak-20260912-wake /opt/dshs/src/supervisor/proxy.ts
cd /opt/dshs && npm run build
# drain + systemctl restart dshs
```
本次改动**只影响注入脚本内容**,不涉及路由、权限、等待时长 —— 回滚面极小。