feat(auth): 注册页人机验证 + 邮箱验证码;品牌标识去 DeepSeek(附域名迁移线 序㊿ 补提交)
三条线合并入库 —— 均已完成并上线(源码与生产一致,此前只部署未入仓)。 ⚠️ 其中域名迁移线为**另一会话**产出,本会话只做入库、**未复验其正确性**(它自报零回归)。 【档案 134 · 注册页人机验证 + 邮箱验证码】 - DB 迁移 v10:users.email(唯一索引 LOWER(email))+ email_codes 事件表(2 索引) - 新增模块 src/web/{register-guard,mail,turnstile,email-code}.ts - routes/auth.ts:新增 GET /api/auth/register/config、POST /api/auth/register/email-code; 注册接口加人机验证与验证码校验;config.ts 新增 12 项配置(默认空 ⇒ 不配 = 老行为) - 邮件走**可插拔驱动**(brevo/http/log),发件人 [email protected](Brevo 域名已认证 + DKIM + SPF) - 防爆破:三层配额(邮箱 6/h、8/天;IP 20/h;全局 200/h)+ 递增冷却阶梯 (60→60→180→300→900→1800s)+ 试错 5 次作废 + 码只存哈希 + 单次使用 + 与用户名绑定 - Turnstile 服务端校 **success + action + hostname 三项**:sitekey 是公开的, 只校 success 时"拿我们的 sitekey 在自己站点替真人取合法 token 再打我们接口"这条路是通的 - 新增 test/register-guard.test.mjs(19 用例) 【档案 137 · 品牌标识改造 — 去 DeepSeek 图形】 - login/register/admin 页头:删 DeepSeek 鲸鱼图标 + 「DeepSeek」文字图形 → 平台标识(中文「能力枢纽」/英语及其他语言「CapabilityNet」,走 i18n 词条 brand.name) - portal 顶栏换图标(页面名「管理门户」保留) - 新建 web/favicon.svg(平台自有 hub 图标,避开 DeepSeek 蓝)+ 四页 favicon 指向它 - 新增 test/i18n-brand.test.mjs(node:vm 跑真实 i18n.js,六条语言路径断言渲染结果) - scripts/verify-static.mjs 新增 SVG 段:XML 注释不得含 ASCII 双连字符(否则整份 SVG 解析失败、图标静默不显示 —— 实际踩到过) - 🔴 会话页面(实例内官方 dsh 界面)的标识**按用户要求未动**(也受 R2 约束) 【档案 135/136 · 域名迁移线(另一会话产出)】 - 域名收敛为 ai1net.com;旧域 alotbuy.com 降级为 301 过渡装置 - src/net/relay/{addr-override,directory,rendezvous,switcher}.ts 种子与候选链更新; src/web/server.ts、src/worker/relay-tunnel.ts、scripts/verify-cluster-domain.mjs - 档案 136 = 控制面按两台中继取并集(**已立项、未落地**) 验证(本会话两条线):新增单测 21 条全通过|全量 221 pass / 0 fail / 1 skipped| verify-static 全合格|其余 10 个 verify 脚本全 OK|线上实测:Turnstile 假 token 403、 发码 delivered、四页 deepseek 命中 0、favicon 200。
This commit is contained in:
1 parent
d2ef362a98
commit
971ccc3703
56 files changed
+3498
-193
No files matched your search
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: dsh-change-workflow
|
||||
description: dsh 多租户平台(alotbuy.com / dshs,服务器 47.77.182.89)功能改造与优化任务的完整工作流。当用户提出平台需求、改造、优化、缺陷修复、功能调研,或要求"按既有流程执行/沉淀/整理处理机制"时触发。核心:需求识别→调研→规划→开发→验证→归档清理 六阶段 + 红线机制(不自动升级 dsh / 不改官方主程序 / client bundle 禁 exports.default / 不用真实账号测登录 / **R7 禁未经确认的批量·全仓写入** / **R8 会中断在线用户的生产变更须先知会**)+ 备份先行 + append-only 日志 + 表格式交付 + 文档「服务器唯一源 + docs-status 单目录同步」。
|
||||
description: dsh 多租户平台(ai1net.com / dshs,服务器 47.77.182.89)功能改造与优化任务的完整工作流。当用户提出平台需求、改造、优化、缺陷修复、功能调研,或要求"按既有流程执行/沉淀/整理处理机制"时触发。核心:需求识别→调研→规划→开发→验证→归档清理 六阶段 + 红线机制(不自动升级 dsh / 不改官方主程序 / client bundle 禁 exports.default / 不用真实账号测登录 / **R7 禁未经确认的批量·全仓写入** / **R8 会中断在线用户的生产变更须先知会**)+ 备份先行 + append-only 日志 + 表格式交付 + 文档「服务器唯一源 + docs-status 单目录同步」。
|
||||
version: 2.9.2-playwright-banned
|
||||
updated_at: 2026-09-15
|
||||
last_change: 【2026-09-12 新增「三把锁」落地机制 + 更正过期指向】把「多任务并行调度协议」从**设计**补成**可强制执行的判据** —— 全局执行锁 `交接单/.exec-lock`(同一时刻只允许一个执行会话)+ 单级占用锁 `.doing-<单号>` + **服务器侧操作锁 `/opt/dsh/state/.op-lock/`**(管生产态:重启 / drain / 改实例 env·quota / 批量铺插件 / 改 nginx·nft·证书,因「服务器态变更看不出来」故必须显式加锁);工装 `scripts/handoff-guard.sh`(新增【1d】分支)与 `scripts/op-lock.sh`。同时**更正产出闭环里的过期指向**:原文写 `docs-status/文档库状态备注.md` + `bash scripts/docs-status-sync.sh --pull`,**该机制已不存在** → 改为 `INDEX.md §二` 登记 + 收尾四件套(audit → manifest → sync-check → `PUSH=1 handoff-guard`)。出处:当日 3 次并行事故 + `04-调整方案/69`。此前】**R7 禁止未经确认的批量 / 全仓写入**(由来:为"让 scp 出去的文件行尾干净",用脚本把 **147 个文件** CRLF→LF —— 当期只被要求改一个 UI 字符串;被用户指为「容易把服务器搞崩」,且随后 `cp -r` 确实把**未改动的** 2 个文件覆盖到了服务器)+ **R8 会中断在线用户的生产变更须先知会**(由来:档案 58/59 连续两次重启,直接引发用户"会话连接异常"报障)。【2026-09-11 晚,用户当面纠正后大修】① **阶段 0 新增第 0 条「开工前置检查」并标明"本技能最重要的一步"**(装工具/写自动化/查机制前先看:可用技能列表 → 本机/项目已有技能与记忆 → "本机已有的能否满足")+ 红线 **R6「先查已有资产,再动手」**;② **浏览器自动化定型**:用 `browser-harness`(CDP 9223,Windows 调用方式 + helper 清单),**禁再装 agent-browser / Playwright**;⛔ **铁律:browser-harness 附着的是「用户正在用的 Chrome」→ 动手前先 `list_tabs()`,不是全新空浏览器就立刻停手**(本轮真实事故:在其浏览器里登录测试号 → 覆盖用户 `.alotbuy.com` 的 `sid`);dsh composer 是 Lexical(`data-lexical-editor`),`type_text`/`fill_input` 均未生效;③ **更正「空闲回收」认知**:回收**从未生效**(TTL 默认 7 天 / cap 4 / 每请求 touch),但 **"回收没生效"≠"实例不会中断"** —— 中断真凶 = **服务重启(09-11 达 48 次)→ 旧实例变孤儿 → 另起新实例 → 旧页面 401** + **实例崩溃自动重启(近 4 天 69 次;admin 根因 `duplicate loader entry id: permission`)**;附三数核对手法;④ 阶段 1 新增「**用户报障第一步:先读 journal 拿真实失败请求**」(三分钟定位,判据速查 401/502/crash);⑤ 阶段 4 新增「**验证用例必须同时覆盖「有请求体」与「无请求体」**」(档案 51 事后修正实证:只测 POST 会整条漏掉 GET/SSE)+ 「模拟浏览器语义要测到底」(只带最初旧 cookie、显式断言 Set-Cookie 回写);⑥ R4 补**专用测试账号模板**(`role='active'`、`approved_by` 需真实 admin id;`users` 表无 `status` 列)。此前新增「**本环境 TLS 走中间人代理 → Python 脚本必须 SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt**」(两份 CA bundle 的区别 + 零平台改动正解 + 为何不走 R5 注入 env)+「职责边界:技能投放=admin 的事,平台只提供机制与把关」;此前 R5 追加「**安装类操作=扩大,必须先出安装确认清单(七项)**;技能/插件依赖体检只报告+拦截、绝不代装」;另把「复杂引号一律本地写文件→scp(禁 ssh 内 inline node -e/sed)」从建议升为**硬性要求**(本轮同类错误连犯 4 次)。此前补环境事实「正确域名 alotbuy.com(dsh.alotbuy.com 是旧域 301,抓页面只会拿到 301 HTML)」+ 档案 45 存量会话提示落点(login.html);此前新增「**基础运行时版本冻结**」节(档案 44:唯一缝隙=Python user-site 会盖平台包;修法 PYTHONNOUSERSITE + PYTHONUSERBASE 双保险;版本漂移巡检 scripts/runtime-baseline.cjs + cron 05:10);补「3.6 不开放」的正解(引导优先,遮蔽不可行)+ 属主自愈(ws-cleanup --reclaim-only,平台 root 跑 pnpm 污染用户 ws);**R5 已立**。此前新增红线 R5「权限可见面只准收窄,扩大必须先确认」**(含「R5 权限扩大门禁」整节:扩大/收窄方向判定表、权限影响评估四问、触发文件清单、以「实例可访问路径清单」做 diff 验收);阶段 2 红线自查与影响评估同步加入 R5 门禁;此前新增「**无法遮蔽 /usr 内文件**」(ro-bind 目录无法创建挂载点 → 叠加遮蔽会让实例起不来;判据「只有先 tmpfs 再白名单的目录才可自由增删」+ 改 bwrap 参数后必须真启动实例验证)+「**实例Line truncated
|
||||
@@ -13,12 +13,12 @@ dsh 多租户平台(服务器 47.77.182.89,dshs + dsh 0.1.2-rc.1)功能改
|
||||
|
||||
## 平台速查(硬编码事实,勿猜)
|
||||
|
||||
> ⚠️ **正确域名 = `alotbuy.com`**;`dsh.alotbuy.com` 是**旧域名、nginx 301 跳转**
|
||||
> ⚠️ **正确域名 = `ai1net.com`**(2026-09-19 由 `alotbuy.com` 迁入);`alotbuy.com` 是**旧域名、已降为 nginx 301 过渡装置**
|
||||
> ——对它抓页面只会拿到 301 HTML,**别误判为"改动没生效"**(2026-09-11 踩过)。
|
||||
> 平台页由门户直出(`127.0.0.1:3080`),生效副本只有 `/opt/dshs/web/` 一份。
|
||||
> 存量会话提示(档案 45)落在 `web/login.html`:告知「shell 频繁要求审批 → **新开一个会话**即可」。
|
||||
>
|
||||
> ⚠️ **`*.alotbuy.com`(含每个用户子域)都由同一个 vhost 先 `proxy_pass 127.0.0.1:3080`(门户)**,
|
||||
> ⚠️ **`*.ai1net.com`(含每个用户子域)都由同一个 vhost 先 `proxy_pass 127.0.0.1:3080`(门户)**,
|
||||
> 再由门户的 `src/supervisor/proxy.ts` 转发到实例 → **想改"子域访问行为"要改 `proxy.ts`,
|
||||
> nginx 的 `error_page` / `location` 到不了实例层**(档案 49 实测)。
|
||||
> 实例空闲回收后首次访问的链路(档案 49):**导航**(GET+`Accept: text/html`)→ 立刻 302 到
|
||||
@@ -73,7 +73,7 @@ dsh 多租户平台(服务器 47.77.182.89,dshs + dsh 0.1.2-rc.1)功能改
|
||||
> 🔴 **浏览器自动化:只允许两件工具;Playwright 全面禁止**(用户 2026-09-13 明令:「**playwright 禁止使用,加到规则中**」):
|
||||
> ① **`agent-browser`**(WorkBuddy 内置技能,**独立浏览器,不碰用户环境**)—— **本项目默认走这条**(2026-09-13 选定);
|
||||
> ② **`browser-harness`**(CDP **9223**,**附着用户正在用的 Chrome**)—— 只在需要看用户现场时用,
|
||||
> ⛔ **动手前必须先 `list_tabs()`**;旧事故:在其浏览器里登录测试号 → **覆盖了用户的 `.alotbuy.com` 的 `sid`**。
|
||||
> ⛔ **动手前必须先 `list_tabs()`**;旧事故:在其浏览器里登录测试号 → **覆盖了用户的 `.ai1net.com` 的 `sid`**。
|
||||
> ⛔ **禁止 Playwright / `playwright-core`**(含「独立无头」用法)—— 本行**更正**此前把它写成「只读验收默认路径」的错
|
||||
> (那是 2026-09-11 加的,与同页「禁 Playwright」**自相矛盾**;2026-09-13 已按用户明令统一为**禁止**)。
|
||||
> 静态页与 API 取数优先 `curl` / WebFetch,**别动浏览器**。
|
||||
@@ -83,7 +83,7 @@ dsh 多租户平台(服务器 47.77.182.89,dshs + dsh 0.1.2-rc.1)功能改
|
||||
| 服务器 | 47.77.182.89(Alibaba Cloud Linux al8);SSH:用别名 `bt-server`(`~/.ssh/config`:`HostName 47.77.182.89`、`Port 32022`、`User root`、`IdentityFile ~/.ssh/id_ed25519`) |
|
||||
| 门户 | dshs(systemd `dshs`,127.0.0.1:3080);源码 `/opt/dshs`(git master) |
|
||||
| 数据 | `/var/lib/dshs/users/<id>/{home,ws}`;DB `/var/lib/dshs/dshs.db`(root 600) |
|
||||
| 域名 | **alotbuy.com**(门户,CF 代理 + 通配证书,源站 443);用户子域 **`<用户名>.alotbuy.com`** 经 portal proxy 转发实例;旧 `dsh.alotbuy.com` 301 → `alotbuy.com`(档案 22 域名迁移) |
|
||||
| 域名 | **ai1net.com**(门户,CF 代理 + 通配证书,源站 443);用户子域 **`<用户名>.ai1net.com`** 经 portal proxy 转发实例;旧 `alotbuy.com` 301 → `ai1net.com`(档案 22 / 2026-09-19 迁入) |
|
||||
| 实例 | 门户 spawn `node /usr/local/bin/dsh --profile web --host 127.0.0.1 --port <动态>`;ISOLATION account(setpriv,uid∈[100000,199999];admin=114801) |
|
||||
| 文档 | **本机 git 工作树 = 唯一源**:`E://ProgramData//AI技能//aliyun-dsh-server//dsh-server-docs`(仓库 `dsh_shenxian_doc`)→ scp 到服务器 `/opt/dsh/docs`(root 600 / README 644,**无 .git 的部署镜像**)。⛔ **本行 2026-09-13 更正**:原文写「服务器唯一源 / 本地不保留副本 / `dsh-server-docs/` 已废弃」——那是**更早的状态,现已作废** |
|
||||
| 文档同步 | 改完跑**四件套**:`docs-audit.py` → `docs-index-stats.py --write`(INDEX 状态摘要**机器生成**)→ `docs-manifest.py` → `docs-sync-check.sh` → `docs-consistency.py`;**只 scp 自己本次改的文件**、推前复跑对账。⛔ `docs-status/` 机制**已不存在**(原文作废) |
|
||||
@@ -163,7 +163,7 @@ dsh 多租户平台(服务器 47.77.182.89,dshs + dsh 0.1.2-rc.1)功能改
|
||||
4. 本机直连验证(绕 DNS/代理):
|
||||
```bash
|
||||
# SECURE_COOKIES=true → 必须走 TLS;用 --resolve 把域名指到回环
|
||||
curl -sk --resolve alotbuy.com:443:127.0.0.1 https://alotbuy.com/api/...
|
||||
curl -sk --resolve ai1net.com:443:127.0.0.1 https://ai1net.com/api/...
|
||||
# 带登录态:-H "Cookie: sid=<token>"
|
||||
```
|
||||
(明文 `http://127.0.0.1:3080` 在 SECURE_COOKIES 下不通)
|
||||
@@ -205,13 +205,13 @@ dsh 多租户平台(服务器 47.77.182.89,dshs + dsh 0.1.2-rc.1)功能改
|
||||
|
||||
**browser-harness 会附着到本机**正在运行的**用户日常 Chrome**(CDP **9223**)——2026-09-11 实证:
|
||||
`BU_CDP_URL=9223` **不被尊重**(daemon 常驻共享,`BH_RUNTIME_DIR_SHARED=1`),换 `BU_NAME` + 非共享 runtime 重建 daemon 后
|
||||
`list_tabs()` **仍返回用户的 16 个标签页**(`admin.alotbuy.com` / `guest.alotbuy.com` / `portal.html#/plugins/manual`);
|
||||
`list_tabs()` **仍返回用户的 16 个标签页**(`admin.ai1net.com` / `guest.ai1net.com` / `portal.html#/plugins/manual`);
|
||||
`curl --noproxy '*' http://127.0.0.1:9223/json/list` 直接证实 **9223 就是用户浏览器的调试端口**。
|
||||
|
||||
**所以:脚本第一步永远是 `print([t["url"] for t in list_tabs()])`。**
|
||||
- 只要看到的**不是**你自己新开的空浏览器 → **立刻停手**,只做只读检查。
|
||||
- **禁止**在未确认隔离前做任何有副作用的动作(登录 / 输入 / 提交 / 关闭标签)。
|
||||
- 已造成的真实事故:在用户浏览器里 `fetch('/api/auth/login')` → **覆盖其 `.alotbuy.com` 的 `sid`**,用户在该浏览器里变成测试账号身份。
|
||||
- 已造成的真实事故:在用户浏览器里 `fetch('/api/auth/login')` → **覆盖其 `.ai1net.com` 的 `sid`**,用户在该浏览器里变成测试账号身份。
|
||||
补救 = 删掉测试账号(cookie 失效 → 用户重新登录即可)。**收尾必须 `close_tab` 关掉自己开的标签。**
|
||||
|
||||
### 正确调用(Windows,2026-09-11 实测可用)
|
||||
@@ -269,7 +269,7 @@ dsh 多租户平台(服务器 47.77.182.89,dshs + dsh 0.1.2-rc.1)功能改
|
||||
```
|
||||
|
||||
**登录态**:DB 直插临时 session(`user_agent=poc-ui`,用后即删),取 token 后
|
||||
`cdp("Network.setCookie", name="sid", value=TOKEN, domain=".alotbuy.com", path="/", secure=True)` → `Page.reload`。
|
||||
`cdp("Network.setCookie", name="sid", value=TOKEN, domain=".ai1net.com", path="/", secure=True)` → `Page.reload`。
|
||||
用户侧真实浏览器完全不受影响(不覆盖其 sid)。
|
||||
|
||||
**三个必踩坑**:
|
||||
|
||||
Reference in new issue
Block a user