admin
|
c2b7c5ef71
|
平台共享模型改为「管理员逐用户授权」+ 品牌中文名改「能力网络」+ 修掉跨机模型落地/语言偏好静默失效
三条线合并入库(同一次部署批次,源码与生产此前已一致):
一、档案 138 · 平台共享模型:管理员逐用户授权(默认关闭)
用户口径原文:「admin 设置的共享模型,需要 admin 在用户列表中开启(新增选项,默认关闭),
用户才能在会话中使用(以及在设置的模型设置页面展示)」。
· DB 迁移 v11:新增 users.shared_model_granted(DEFAULT 0 = 默认关闭)。
⚠️ 刻意**不**复用 v6 的 shared_model_enabled —— 那是用户侧偏好(用户能自己关,默认 1),
而本需求要的是**管理员门禁**;共用一列则用户点一下就给自己授权,门禁形同不存在。
生效 = granted ∧ enabled(server.ts#sharedLandingRows)。
· 新路由 POST /api/admin/users/:id/models/shared(requireAdmin)+ 审计 shared_model_grant
+ **尽力而为**重启该用户实例(租约被占/实例未运行都不算失败)。
· admin 用户列表新增「共享模型」列;用户侧 /api/me/keys 增 shared.granted / sharedModelGranted;
插件 0.3.24:未授权时「平台共享模型」整块不渲染。
二、顺带修掉一个既有真缺陷:平台侧写用户 home 必须走 UserFs(用户卷在 worker 上)
landModels 原先 join(owner.home_dir, …) + 本机 fs ⇒ 对「实例不在控制面本机」的用户
读到空串(**不报错**)⇒ 模型落地一直是**静默空操作**(托管清单还被清空)
——即档案 87 的模型设置页对 guest 这类用户**从未生效**。
· UserFs 新增 readHomeFile/writeHomeFile + 文件名白名单(settings.yaml / .credentials.yaml)
· worker agent 新增 /fs/home-read /fs/home-write(只认白名单裸文件名)
· landModels 改走 userFs(与"文件面"同一份按归属路由 ⇒ 落地与实例必然同机)
· 同轮把 /api/me/locale(档案 102 语言偏好)也改成同一套(原先同样失效)
· home-files.ts 抽出 backupHomeFile(备份留平台侧,命名规则逐字不变)
三、档案 139 · 品牌中文名:能力枢纽 → 能力网络(其他语言仍 CapabilityNet)
落点四处:i18n 中文词条 / admin.html 顶栏 / favicon.svg 的 title+aria-label / design.css 注释;
test/i18n-brand.test.mjs 期望值同步。档案 137 顶部加"后续"指针,不改历史。
验证(全部真机实测):
· 红腿:未授权 → 106 上 guest 的 .credentials.yaml refs 变空(共享 key 被撤)
· 绿腿:授权 → key 回来 + 托管清单恢复 ["DEEPSEEK_API_KEY"]
· admin 列表带出 sharedModelGranted;用户侧 granted 随授权翻面(true/shared ↔ false/none)
· 开关两次均 200(不再假失败);插件实装 0.3.24 且含 gating 字符串
· 语言偏好:106 上 settings.yaml 出现 locale.preference=en(属主=实例属主,既有段逐字保留)
· 本机 npm test 226 tests / 225 pass / 0 fail / 1 skipped;四个 verify 脚本全绿
部署:47 推 51 个 lib 产物、106 推 12 个(lib/ 是 gitignore ⇒ 回滚点物化在
/opt/dsh/backups/seq138b-20260919-125345/,逐文件对账 0 不一致;先 106 后 47);
插件 business-plugins 0.3.24(两机 artifacts 与本机 pack md5 一致)。
|
2026-09-19 13:51:01 +08:00 |
|
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 |
|
admin
|
146c3d25ef
|
feat(overlay): 覆盖网络线序①–⑮ 代码与测试产物入库
覆盖网络线累积产物(此前只在工作区、未入版本库):
- 新增 relay 子系统 src/net/relay/**(wire/duplex/server/client/dialer/switcher/directory/identity/keys/placement/network/addr-override/main/index)
- 新增 src/worker/relay-tunnel.ts、src/web/routes/overlay.ts
- 新增观测/演练脚本 overlay-probe、overlay-failover-drill、overlay-keyring、overlay-holepunch、overlay-jitter、overlay-wan、overlay-relaykey-add、relay-mem-calibrate
- 新增测试 12 个(relay / relay-failover / remote-spawner / instance-port / overlay-{network,auth,bootstrap,identity} / remote-user-fs 等)
验收基线:npm test = 162 pass / 0 fail / 1 skip;--scene all = 12 PASS / 0 SKIP / 0 FAIL;overlay-probe = 12/12
|
2026-09-17 17:03:27 +08:00 |
|
admin
|
c70d5d860e
|
feat(cluster): 集群化落地 —— Manager/Worker 拆分 + 归属租约 + 跨机验证(T08)
背景:把平台从「单机单进程」改造成「1 组 Manager + N 台 Worker + 共享归属状态」,
硬约束 = 全程兼容单例模式(deployMode 默认 local;生产切换前 47 一行未动)。
主要改动
1) 数据模型 v7(SQLite 与 PG 两方言同步):新增 dsh_hosts 注册表 +
dsh_instances.{host_id,epoch,heartbeat_at,lease_until};claimInstance 原子抢占
(UPDATE … WHERE host_id IS NULL OR lease_until < now)+ pinInstanceHost 钉住归属。
2) 租约与 fencing:src/supervisor/lease.ts(acquire/renew/release + stillHolder 判据 +
ttl > 2×renew 硬校验);心跳里续租,失权即向 worker 下发更高 epoch(self-fencing)。
⚠️ release 只清租约(lease_until),**保留 host_id** —— host_id 是「用户数据在哪台」的锚点。
3) Worker agent(src/worker/agent.ts,子命令 dshs worker):实例生命周期 + 文件面 /fs/*
+ 幂等键(operationId)+ 鉴权(timingSafeEqual);Worker 不写控制面数据
(apiKey/uid 由 Manager 随 launch 投递,R5 收窄)。
4) 远端 Spawner + LeasedSpawner:按 host 路由(**粘性优先**:有历史归属且那台 up 就留在原地,
否则按容量选最空的)+ 容量准入 + deployMode=cluster 装配(systemd drop-in,可回滚)。
5) bwrap 修正:**所有挂载点的中间目录统一前置 + 去重 + 由外到内**(「就近创建」会在嵌套前缀下
遮掉已绑挂载点 ⇒ bwrap: Can't chdir);且**只能用 --tmpfs**,用 --perms 会让 47 的
bwrap 0.4.0 直接拒启动(沙箱全挂)。
6) 跨机隧道 src/worker/tunnel.ts:SSH ControlMaster + 动态 -R 转发;**自愈由 agent 本地
20s 定时器驱动**(不能只放 /healthz —— 心跳本身经隧道进来,断了就没人触发它)。
7) 文件面按归属路由(RemoteUserFs):实例与文件必须落在同一台机器,否则实例看不到自己的文件。
8) 观测面:dshs doctor / dshs cluster status。
验证(本次均已实跑)
- test/lease.test.mjs:SQLite 10/10 == PG 10/10
- 组件级端到端 5 个:verify-cluster-{agent,lease,fs,migrate,live}.mjs
- 真跨机(47 Manager / 106 Worker,跨云 + 反向隧道)verify-cluster-cross.mjs 九步全绿
- 域名形态访问 verify-cluster-domain.mjs(<user>.域名 → Manager → 远端实例;越权 403)
- 冒烟 scripts/smoke-*:6/8,失败项与改动前基线完全相同(无回归)
- 生产切换与回滚剧本见 dsh-server-docs/交接单/T08-集群化落地-兼容单例模式.md §16
|
2026-09-15 18:47:02 +08:00 |
|
admin
|
918f1d3a51
|
feat(models): 平台自建「模型设置」—— 用户自配厂家 / 条目各自开关 / 共享模型开关(档案 87)
- 官方「设置 → 模型」页在平台环境**必然报错**(判据在**浏览器页面**的 loopback 判定;官方 README 原文 Non-loopback pages get no durable settings)⇒ 该分区对全角色(含 admin)隐藏,用户自配改走平台自建页(插件 0.3.11)
- DB 迁移 V6:credential_vault.route/base_url/api/models + users.shared_model_enabled;并**重定义 getEnabledCredentialKeyRef**(互斥删除后原实现无 ORDER BY ⇒ 「任取一条」)
- 新落地层 src/web/model-landing.ts:spawn 时把「已启用条目」写进实例 .credentials.yaml 与 settings.yaml 的 llm-pi-ai.providers.<route>;字段名与官方包实测对齐(apiKeyEnv / baseURL / api,**不是** protocol);只碰自己写过的 + 一次性交接
- 接口 /api/me/keys、/api/me/keys/:id/toggle、/api/me/models/shared;前端新增「设置 → 模型设置」分区(settings.section id=model-settings / order 100 / 全角色)
- 顺手修两处:ensure-role-profile-patch.cjs 的 --force 整文件覆盖会抹掉 admin 的 disable-hmr 与 workspace-scoped-picker 两个平台块(改为 stripManagedBlock 只替换自己那段);verify-mem-model.mjs 因档案 86 重构而长期失败的陈旧断言
⚠️ 本提交同时包含**档案 86(admin 跨用户实例管理 + 两处改名)**的代码改动 —— 该部分已上线并端到端验证;其 import/register 与本次改动同处 src/web/server.ts、poc/business-plugins/lib/client.js 等文件,按**文件粒度无法拆分**,且不带它会让仓库 tsc 直接失败(缺 src/web/routes/admin-user-ops.ts)。
|
2026-09-14 00:00:15 +08:00 |
|
admin
|
43976fea6a
|
初始提交:DSH 多租户平台(dshs)
|
2026-09-13 16:18:10 +08:00 |
|