129 lines
12 KiB
Markdown
129 lines
12 KiB
Markdown
# 135-旧域 alotbuy.com 从项目移除(2026-09-19 落地)
|
||||
|
|
|
|||
|
|
> **一句话**:平台对外域名**收敛为 `ai1net.com` 单一域名**;旧域 `alotbuy.com` 在**代码 / 服务端配置 / 文档技能**三层全部移除,
|
|||
|
|
> 旧域 nginx 站点**降级为 301 过渡装置**(保留 relay 透传与 ACME,30 天零流量后退役)。
|
|||
|
|
> **触发**:用户 2026-09-19 原话 —— 「旧域 alotbuy.com 怎么处置:**从项目中移除后续都用新域名**」
|
|||
|
|
> **执行**:序㊿ 执行棒(会话名 `旧域移除-控制面并集-20260919`)· 计划书 = 工作区根 `_tmp_dompurge/PLAN-旧域移除与并集立项_20260919.md`
|
|||
|
|
> **姊妹件**:同棒产出的立项交接单 = `04-调整方案/136-控制面按两台中继取并集-立项交接单.md`
|
|||
|
|
|
|||
|
|
## 背景与动机
|
|||
|
|
|
|||
|
|
1. **域名已在 09-19 切换完成**(见 `ops/域名迁移_ai1net_20260919/RUNBOOK-cutover.md`),但切换只做到"新域能用",
|
|||
|
|
旧域仍以**独立门户站点**形态存在 ⇒ 两套入口并存 = 两套证书、两处签名目录、两处排障路径。
|
|||
|
|
2. **旧域残留会让判据撒谎**:演练脚本的"是否切到 47 那台中继"判定用**硬编码域名字符串**匹配端点 ⇒
|
|||
|
|
域一改,**真实发生的切换会被判成失败**(本节 §"实测踩坑 1" 即此坑)。
|
|||
|
|
3. **relay 候选里存在"摘名不同、物理同一"的伪多样性**:`alotbuy.com` 与 `relay-direct.alotbuy.com` 摘名不同、
|
|||
|
|
**同落 47 同一路径**、且后者还开了 `DSHS_OVERLAY_ADDR_OVERRIDES` 直连 IP ⇒ 5 条候选里 2 条**零物理多样性**。
|
|||
|
|
|
|||
|
|
## 用户决策
|
|||
|
|
|
|||
|
|
| 时间 | 用户口径 | 落地 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 2026-09-19 | 「从项目中移除后续都用新域名」 | 代码 / 服务端 / 文档技能三层真实替换;**旧域不删站、降级 301**(删站会打断仍指向旧域的客户端,净变差 ⇒ R11 不允许) |
|
|||
|
|
| 2026-09-19 | 「A 立项修」(控制面并集) | 只出立项交接单(档案 136),**本轮不落地** |
|
|||
|
|
|
|||
|
|
## 范围分桶(最终实测结论)
|
|||
|
|
|
|||
|
|
| 桶 | 对象 | 处置 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| **A 代码 / 运行时** | `src/**`、`web/**`、`test/**`、`scripts/**` | ✅ 全量替换(10 个源文件 + 2 个 test + 2 个 live 校验脚本 + 1 个演练脚本 + 1 个探针注释) |
|
|||
|
|
| **B 服务器配置** | 47 的 drop-in / nginx;106 的 worker env | ✅ 落新值(见 §执行记录 S3/S4) |
|
|||
|
|
| **C nginx 旧域站点** | 47 `/etc/nginx/conf.d/alotbuy.com.conf` | ✅ 降级为 **301 过渡装置**(保留 3 条非 301 路径) |
|
|||
|
|
| **D 文档 / 技能** | 文档库 + `skills/**` | ✅ 42 处精确替换 + 1 处补 Edit(**只改"现行事实"章节**) |
|
|||
|
|
| **E 不动的** | `04-调整方案/**` 历史档案、`archive/**`、参数表 §11 历史补记 | ⛔ 历史记录**不改**(改了就是伪造历史) |
|
|||
|
|
| **F 排除** | `work.alotbuy.com`(Gitea 仓库服务) | ⛔ **不是本平台域名**,与迁移无关 |
|
|||
|
|
|
|||
|
|
## 执行记录(S1–S7)
|
|||
|
|
|
|||
|
|
**S1 · 代码层替换(A)**
|
|||
|
|
- 扫描 127 个文件,命中并改写 10 个:`src/config.ts`×2、`src/net/relay/addr-override.ts`×4、`src/net/relay/directory.ts`×2、
|
|||
|
|
`src/net/relay/rendezvous.ts`×1、`src/net/relay/switcher.ts`×4、`src/web/server.ts`×3、`src/worker/relay-tunnel.ts`×5、
|
|||
|
|
`test/overlay-bootstrap.test.mjs`×22、`test/relay-failover.test.mjs`×4、`web/wake.html`×1。
|
|||
|
|
- ⚠️ **计划书的行号表是快照、已漂移**(例:计划写 `config.ts:312`,实测 `:381`;另有计划未登记的 `config.ts:125`、`directory.ts:324`)
|
|||
|
|
⇒ 一律**先自己 scoped grep、再做字节级精确替换**,⛔ 不按计划行号下手。
|
|||
|
|
|
|||
|
|
**S2 · 修掉一个真 bug(A3)**
|
|||
|
|
- `web/wake.html` 里有**转义点写法** `alotbuy\.com`,被批量替换漏掉 ⇒ 白名单正则仍只认旧域 = 唤醒后**跳转白名单恒不命中**。
|
|||
|
|
- 修为新域后缀(单后缀即正确 —— 已核 `next` 是**由请求 host 拼出的绝对 URL**,见 `src/supervisor/proxy.ts:613`):
|
|||
|
|
`/^(\^|\.)ai1net\.com$/.test(x.hostname)`(源码中无多余反斜杠)。
|
|||
|
|
|
|||
|
|
**S3 · 47 服务端(B + C)**
|
|||
|
|
- drop-in `overlay-443fb.conf`:`DSHS_OVERLAY_BOOTSTRAP_SEEDS` **5 → 3**,删掉**同机同路径**的两条旧域候选;
|
|||
|
|
`DSHS_OVERLAY_ADDR_OVERRIDES=relay-direct.ai1net.com=47.77.182.89`;注释写明"旧域 `/dshs-relay` 透传仍在"与回滚原文。
|
|||
|
|
- nginx 旧域站点降级:`map $host $alotbuy_301_host`(默认 `ai1net.com`;`~^(?<lb301>[^.]+)\.alotbuy\.com$ → $lb301.ai1net.com`
|
|||
|
|
⇒ **子域对子域映射**),80/443 双 server 全 301;**仅保留 3 条非 301 路径**:
|
|||
|
|
`/.well-known/acme-challenge/`、`/dshs-relay` → `127.0.0.1:20080`、`= /dshs-overlay/bootstrap` → `127.0.0.1:3080`。
|
|||
|
|
- 文件头写入**降级而非删除的理由**与**退役条件 = 30 天零流量**。
|
|||
|
|
- 归档 `_pending-ai1net/` → `mv` 到 `/root/_dompurge/_pending-ai1net-archived-<ts>`(**不是删除**)。
|
|||
|
|
|
|||
|
|
**S4 · 106 worker(B)**
|
|||
|
|
- `/etc/dshs-worker.env` 第 26 行同步为同 3 条 SEEDS;备份 `/etc/dshs-worker.env.bak-dompurge-20260919-073149`;`daemon-reload`。
|
|||
|
|
- 顺带取证:`dshs-relay-client` 单元为 **disabled**(历史遗留、无害)—— **只报告、未动**。
|
|||
|
|
|
|||
|
|
**S5 · 控制面二次重启以清签名目录缓存(关键一步)**
|
|||
|
|
- 一次重启后 bootstrap **仍是 5 条、仍含 2 条旧域** ⇒ 定位到 Manager 把**自己发布的签名目录**缓存在
|
|||
|
|
`/var/lib/dshs/overlay/directory.json`(`fetchedAt` 07:29,**早于改动**)且**懒重建**。
|
|||
|
|
- 处置:备份 + 删除该**可再生**缓存 → 第二次重启控制面 ⇒ Manager 解析出 **3 条候选、全部新域**
|
|||
|
|
(`source=seed-directory`),缓存重建 **578 B**。
|
|||
|
|
|
|||
|
|
**S6 · 文档与技能(D)**
|
|||
|
|
- 42/42 处精确替换(**每处带 count 断言**):`BRIEF.md`、`README.md`、`INDEX.md`、`DEPLOY-本部署.md`、
|
|||
|
|
`02-运维手册.md`(**仅附录 C = 现行事实章节**)、`docs-manifest.json`、`scripts/docs-consistency.py`、
|
|||
|
|
`scripts/cf-probe.py`(新增 `CREDS={'ai1net.com':'/etc/cloudflare-ai1net.ini'}` + `token(zone)`)、7 个技能文件。
|
|||
|
|
- 补 1 处:`skills/dsh-instance-diagnose/SKILL.md`(`goto https://<user>.ai1net.com/`)。
|
|||
|
|
- 文档库镜像同步:`ops/nginx/alotbuy.com.conf` 换为 301 版(md5 `eccf9014c6e1c3f13a5c32b7b14418a9`,与生产一致)。
|
|||
|
|
- 从 47 暂存目录抢救回 `ops/域名迁移_ai1net_20260919/RUNBOOK-cutover.md`(**唯一副本**),并在头部加状态横幅
|
|||
|
|
(记录 §1 阻塞已解、S1–S5 已执行;原"留待你确认退役时机"**已被本棒取代**)。
|
|||
|
|
|
|||
|
|
**S7 · 参数字典与判据脚本**
|
|||
|
|
- 参数表值格 **2 键**:`PORTAL_HOST_HEADER`、`DRILL_KILLED_MATCH` 由 `alotbuy.com` → `ai1net.com`(🔴 后者是**假红根因**,见踩坑 1);
|
|||
|
|
+ 两处 `-H "Host: alotbuy.com"` 示例 + §6 一条候选摘名说明 ⇒ **§10 指纹同步为 `075a97239c2d1d8a1f442ec6cc787ede`**。
|
|||
|
|
- `scripts/overlay-probe.cjs` 注释、`verify-cluster-domain.mjs` / `verify-platform-admin-section.mjs` 默认域同改。
|
|||
|
|
|
|||
|
|
## 实测踩坑(两条,都是"判据撒谎"型)
|
|||
|
|
|
|||
|
|
**踩坑 1 · 演练"假红"(已修)**
|
|||
|
|
- 现象:`overlay-failover-drill.cjs --scene all` = **11 PASS / 1 FAIL**(基线 12 PASS),红在幕4-A「目标不是 47 那台」。
|
|||
|
|
- 根因:幕4-A/幕4-B 的判定用**硬编码字符串** `alotbuy.com` 去匹配端点 ⇒ 豁免**真的生效了**、真的切回 47,**只是判定认不出**。
|
|||
|
|
- 修法:两处匹配器改为取参数表 `DRILL_KILLED_MATCH`(`m3.includes(killedMatch)` / `m2.includes(killedMatch)`),并在注释块记录本次域迁移的勘误。
|
|||
|
|
|
|||
|
|
**踩坑 2 · 「查旧域不存在」的两种假象**
|
|||
|
|
- 旧域 `/dshs-relay` 直取 = **404**:曾疑似回归 ⇒ **A/B 对照**:新域裸 GET **同样 404**;两域**带 WS 升级头都返回 101**
|
|||
|
|
⇒ 非缺陷(`/dshs-relay` 只对 WebSocket 升级生效)。
|
|||
|
|
- `admin/guest.ai1net.com` = **401**(不是 302):属既有"**无 session 直连实例页**"行为,**非回归**(档案 136 §7 E7 已登记口径)。
|
|||
|
|
|
|||
|
|
## 验收证据
|
|||
|
|
|
|||
|
|
| # | 判据 | 结果 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| V1 | 签名 bootstrap 候选集 | **3 条 / 0 处旧域**,`source=seed-directory`,缓存 578 B |
|
|||
|
|
| V2 | 零回归三件套 · 探针 `overlay-probe.cjs` | **29 PASS / 0 FAIL / 0 SKIP(rc=0)**;`OBS-21` = `count=3 hosts=3`,**47 / 106 两机读一致**,全表零 `alotbuy` |
|
|||
|
|
| V3 | 门户可达 | 新域首屏 **200** |
|
|||
|
|
| V4 | 旧域过渡 | 301 生效,**含子域映射**(`guest.alotbuy.com` → `https://guest.ai1net.com/...`) |
|
|||
|
|
| V5 | relay 透传双域一致 | 裸 GET 双域同 404 / WS 升级双域同 101 |
|
|||
|
|
| V6 | 零回归三件套 · `npm test` | 未跑(**本棒零测试面逻辑改动**;仅字符串与匹配器) |
|
|||
|
|
| V7 | 零回归三件套 · 演练 `--scene all` | ✅ **12 PASS / 0 SKIP / 0 FAIL**(rc=0;**5m56s~6m**;末行原文 `# 结果:12 PASS / 0 SKIP / 0 FAIL`)—— 修后复跑,`幕4-A` 目标 = `wss://ai1net.com/dshs-relay`、耗时 15867 ms / deadline 30000 ms。⚠️ 全套含 120 s + 90 s 观察窗 ⇒ **外层超时必须 ≥ 8 min**,否则 `restore()` 不跑、**106 relay 会留在停机态** |
|
|||
|
|
| V8 | 技能「三处」同步 | ✅ 6 个技能全部本机/文档库/47 镜像三处**域已清**;其中 3 个(decision-method / feature-first / plugin-diagnose)**三处 md5 全同**(`a8971220` / `a1397140` / `3edeeb89`);⚠️ 另 3 个(change-workflow / opensource-release / instance-diagnose)**本机副本更新 ⇒ 存在本会话之外的既存漂移**,本轮**只做域替换、⛔ 未覆盖新副本**(详见 §残留项 6) |
|
|||
|
|
|
|||
|
|
## 回滚
|
|||
|
|
|
|||
|
|
- **代码**:`git checkout -- src web scripts` + `npm run build` ⇒ scp 回 `/opt/dshs/`(产物清单见 `_中间产物_待清理/_证据_序50_部署产物15文件md5_20260919.txt`,15 个文件)。
|
|||
|
|
- **47**:drop-in 恢复 5 条 SEEDS(原文在文件注释里)→ `daemon-reload` → `restart dshs`;nginx 旧域站点恢复原站(备份在同目录 `.bak-*`)。
|
|||
|
|
- **106**:`cp /etc/dshs-worker.env.bak-dompurge-20260919-073149 /etc/dshs-worker.env` → `daemon-reload` → `restart dshs-worker`。
|
|||
|
|
- 回滚后**必须**删一次 `/var/lib/dshs/overlay/directory.json` 并重启控制面(否则又读到旧缓存 ⇒ 假回滚成功)。
|
|||
|
|
|
|||
|
|
## 残留项 / 后续(不阻塞)
|
|||
|
|
|
|||
|
|
1. **旧域退役条件**:`alotbuy.com` 站点保持 301 过渡,**30 天零流量**后再评估删除;`relay-direct.conf`、`dsh.alotbuy.com.conf` 同属"导向新域 / 兜底"装置,**删除即净变差(R11)** ⇒ 保留。
|
|||
|
|
2. **参数表 §11 历史补记**仍引用旧域(含旧证据原文引用)—— **故意不改**(历史记录)。
|
|||
|
|
3. **`dshs-relay-client`(106)** = disabled 历史遗留,无害,未动。
|
|||
|
|
4. **演练脚本修正版**按惯例应两机同源部署(`scripts/overlay-failover-drill.cjs` 是**本机 ssh 出去的工具**,不被任何单元读取 ⇒ 零服务影响)。
|
|||
|
|
5. 控制面**按两台中继取并集** —— 立项见 **档案 136**(本轮只立项,未落地)。
|
|||
|
|
6. **技能副本的既存漂移(⛔ 本会话之外形成,只报告未统一)**:`.workbuddy/skills/` 与文档库 `skills/` 三对不一致 ——
|
|||
|
|
`dsh-change-workflow`(本机 1055 行 vs 文档库 1039 行)、`dsh-opensource-release`(本机 **1.8.2** vs 文档库 1.7.8)、
|
|||
|
|
`dsh-instance-diagnose`(本机 SSH 别名更正 vs 文档库旧写法)。⇒ 本轮对这三份**只做域替换**(⛔ 不覆盖,避免丢内容);
|
|||
|
|
镜像 `/opt/dsh/docs/skills/` 已与**文档库**对齐(md5 同)。**回头条件 = 有人真正要动用这三份技能时**,先做一次副本收敛。
|
|||
|
|
7. **`/opt/dsh/docs` 镜像的更广泛漂移(⛔ 本单范围外,未推送)**:`docs-sync-check.sh`(须 `[email protected]`,默认别名已失效)
|
|||
|
|
报出**30+ 个"仅本地待推送"文件** —— 属**本会话之前**就存在的镜像滞后,本轮**只推了 6 个技能文件**,其余按"只做被明确要求的事"**未动**。
|
|||
|
|
8. **演练脚本 `overlay-failover-drill.cjs` 未铺服务器**:两机 `/opt/dshs{,-cluster}/scripts/` **本来就没有该文件**(它是本机 ssh 出去的工具)
|
|||
|
|
⇒ 只在本机生效;而 `overlay-probe.cjs` 已按惯例**三处同源**(md5 `55568f87bfe2dc8e43289c69b4126004`)。
|