Files
dsh_shenxian/dsh-server-docs/DEPLOY-本部署.md
T
admin 971ccc3703 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。
2026-09-19 09:11:24 +08:00

6.5 KiB
Raw Blame History

DEPLOY — 本部署说明(ai1net.com / 47.77.182.89)

用途:把"这套平台到底部署在哪、装了什么、改了什么、怎么重建/回滚"一次讲清,供后来人(或未来的我)在不翻聊天记录的前提下独立重建。 对应档案 19 §C9。最后更新:2026-09-19(域名迁至 ai1net.com)

1. 拓扑与入口

项 值
服务器 47.77.182.89(Alibaba Cloud Linux 8,阿里云)|SSH 别名 bt-server(端口 32022,root)
门户 / 用户实例 https://ai1net.com / https://<用户名>.ai1net.com(Cloudflare 代理,回源走 443)
旧域名 alotbuy.com(含 *.alotbuy.com、*.dsh.alotbuy.com)→ 301 到新域(2026-09-19 降为过渡装置;relay-direct.alotbuy.com 与 /dshs-relay、/dshs-overlay/bootstrap 仍透传)
面板 宝塔(站点 vhost 由面板管理,注意:面板保存会重写 vhost,可能抹掉自定义指令)
编排器 自研 dshs(Node/TS),systemd 单元 dshs,监听 127.0.0.1:3080
被托管运行时 官方 @deepseek-ai/[email protected](全局安装于 /usr/local/lib/node_modules/@deepseek-ai/dsh,平台不改其任何文件)

2. 目录布局

路径 说明
/opt/dshs 编排器源码仓库(也是 git 仓库;lib/ 被 .gitignore 忽略 → 需构建)
/var/lib/dshs dataRoot:dshs.db(better-sqlite3)、users/<uuid>/
<userRoot> = dataRoot/users/<uuid> 每用户根:ws/(工作区,用户可见)、home/(DSH_HOME:profiles/sessions/storages/skills)、tmp/(1777)
<profile> = <userRoot>/home/profiles/web 该用户的 dsh profile:node_modules/、cordis.patch.yml、package.json、pnpm-lock.yaml
/opt/dsh/ 运维脚本与备份:switch-domain-alotbuy.sh、backups/、artifacts/(插件 tgz)
/www/server/panel/vhost/nginx/ ai1net.com.conf(现役门户)、relay-direct.conf(443/TCP 兜底)、alotbuy.com.conf(2026-09-19 降级为 301)、dsh.alotbuy.com.conf(301)+ 多份 .bak-*
/etc/letsencrypt/live/{ai1net.com,alotbuy.com,dsh.alotbuy.com}/ 证书(新域 DNS-01 via /etc/cloudflare-ai1net.ini;旧域 via /etc/cloudflare.ini)

3. 环境变量(/etc/dshs.env)

键 作用
DSHS_PORT 编排器监听端口(127.0.0.1:3080)
DSHS_DATA_ROOT dataRoot
DSHS_BASE_DOMAIN 门户/用户子域基域(现 ai1net.com)
DSHS_COOKIE_DOMAIN 会话 Cookie 域(现 .ai1net.com;见档案 22 §五 取舍说明)
DSHS_SECURE_COOKIES Cookie Secure 标记(HTTPS 下应开)
DSHS_DSH_BIN 子实例可执行路径
DSHS_ISOLATION_MODE 隔离模式(本部署 account = bwrap + setpriv + systemd-run scope)
DSHS_BASE_UID 每用户 uid 起始值

其余可调项走代码默认值(崩溃退避/熔断、idle-reap、bundled skill dir 等),见 src/config.ts 的 DEFAULT_* 与档案 20。

4. 外部依赖(版本为实测)

依赖 版本 用途
Node 22.23.2 编排器与子实例运行时
pnpm 9.15.9 profile 侧插件安装账本(pnpm-lock.yaml)
bubblewrap 0.4.0 每用户沙箱(合成根 + 只读系统 + 私有 tmp + --unshare-pid)
systemd-run — per-instance scope(MemoryHigh=448M(基础/软限) + MemoryMax=1024M(上界/硬限),与插件开关无关(档案 96)/ CPUQuota=150% / TasksMax=128)
nginx 1.28.3(宝塔) 站点与反代(关键自定义指令见档案 21/22)
nftables — 出网护栏(/etc/nftables-dsh-egress.nft + dsh-egress.service)
sqlite3 CLI 3.26 仅运维排查(平台内置用 better-sqlite3)
certbot + dns-cloudflare — 证书签发/续期(必须 --dns-cloudflare-propagation-seconds 60,否则失败)

5. 平台侧脚本族

脚本 作用
ensure-role-profile-patch.cjs 角色化 profile patch(非 admin 隐藏模型分区/收归插件开关)
ensure-workspace-picker.cjs 目录选择器收敛全量/新用户铺开(写平台段 + 逐用户 pnpm add;幂等,--dry-run/--restart)
scripts/install-workspace-picker.sh <tgz> 逐用户安装插件包(自动判断是否加 pnpm -w)
/opt/dsh/switch-domain-alotbuy.sh 域名切换(改 env → drain → 重启)
/usr/local/bin/provision-new-users.sh 新用户供应(建 OS 账号 + chown + 调用 picker 铺开);由 dsh-provision.path 触发
scripts/ci.sh 类型检查 + 构建 + 单测(不含需要实例/凭据的 smoke)

6. 构建 / 部署 / 回滚(三步)

# 构建(改代码后必做;lib/ 不入库)
cd /opt/dshs && npm run build && bash scripts/ci.sh

# 部署(drain: 停服务 → 清残留实例 → 起服务)
systemctl stop dshs; for p in $(ps -eo pid,user,args | awk '$2 ~ /^dsh-/ {print $1}'); do kill $p; done; systemctl start dshs

# 回滚(示例:回滚到某个提交)
git -C /opt/dshs revert <sha> && npm run build && systemctl restart dshs
  • 本机 ↔ 服务器同步:服务器无 Gitea 凭据 → 服务器 git bundle create → 本机 git fetch <bundle>(路径必须用 D:/…)→ merge --ff-only → push。
  • 文档库:bash scripts/docs-sync-check.sh(双端 md5 对账,0 = 全绿);服务器侧 /opt/dsh/docs(root 600)。

6.5 定时维护(cron,2026-09-11 建立)

任务 文件 时间
存储用量快照 /etc/cron.d/dsh-maintenance 每小时 :05
工作区清理 / 会话回收 / 回收站清空 同上 04:10 / 04:20 / 04:40
平台全量备份 + 旧备份清理 /etc/cron.d/dsh-backup 每周日 05:00 / 05:30

备份脚本 /opt/dsh/backup.sh(入库副本 scripts/backup-platform.sh)含 SQLite 一致性快照;恢复步骤见档案 02 附录 C.8。

7. 默认红线(改任何东西前先读)

  1. 不自动升级 dsh;升级必须走"测试 → 评估 → 修复",并按档案 26 回归清单逐项验。
  2. 不改官方 dsh 主程序与缓存(/usr/local/lib/node_modules/@deepseek-ai/dsh/** 保持原封)。
  3. client bundle 禁 exports.default(R3)。
  4. 未明确要求不擅自 commit/push(服务器 git 与文档 git 都一样)。
  5. 服务器 /opt/dsh/docs 保持 root 600。

相关档案:01(架构)|02(运维,含附录 C 速查)|21(nginx 性能/流式)|22(域名迁移)|26(升级耦合点与回归清单)