前言¶
在 DSH 上跑 Harness 时,常会额外做几个小网页:待办汇总、用量统计、内部工具面板。常见做法是再起一个端口、单独配域名,或再写一套登录。DSH 客户端本身也没有可收藏、可深链的前端路由,刷新后状态容易丢。
dsh-subpages(维护者 SJTUMalPan)是 DSH 的客户端插件:复用 DSH webServer 上的前缀路由(默认 /subpages),提供网关与统一门户,把独立仓库里开发的下级页面挂进来。请求先过 DSH 既有鉴权,不必为新页面单独开端口、单独暴露面。
这是什么¶
一句话:DSH 看板宿主——把独立开发的小网页应用变成 DSH 里的「看板」,共用 DSH 端口与登录态,用一个门户统一进入。
- 仓库:github.com/SJTUMalPan/dsh-subpages
- 社区目录:skillhub.cn/plugins/SJTUMalPan/dsh-subpages
- 包名
dsh-subpages,当前 README 对应版本 0.1.0,MIT 许可证,Node >= 22
核心能力¶
网关与门户¶
浏览器访问 DSH 端口(例如 :3080)下的 /subpages/ 进入门户壳:导航栏、iframe、hash 路由。公共前端在 /subpages/_assets/*(含 subpage.css);已挂载清单由 /subpages/_api/pages 提供。每个 /subpages/<id>/* 请求经 DSH 鉴权(connection.requestRejection)后再转发到看板后端或静态目录。
浏览器 ──> DSH :3080 /subpages/ 门户壳(导航栏 + iframe + hash 路由)
/subpages/_assets/* 公共前端库(subpage.css)
/subpages/_api/pages 已挂载清单(壳用)
/subpages/<id>/* ──► 看板自己的 http 服务(127.0.0.1:<port>)
门户 URL 如 /subpages/notify-hub/todos 可收藏、可深链、刷新不丢——这是相对 DSH 客户端自身路由的补足。
两种看板形态¶
- 代理形态:
subpage.json里target为http://…,宿主反向代理到本机服务;可选health探活(默认/healthz)。 - 静态形态:
target为目录路径,宿主直接投递文件,并对../、符号链接做realpath边界校验。
样式策略:style 为 inherit(默认,宿主在导航类 HTML 里注入公共样式)或 standalone(不注入)。静态页可自引:
<link rel="stylesheet" href="/subpages/_assets/subpage.css">
公共样式基于 Pico 2 classless + 深色 token + sp-* 组件类(.sp-page、.sp-card 等)。
挂载方式¶
目录扫描:在插件包内 pages/<id>/ 下放 subpage.json;也可通过 submodule 指向独立看板仓库(git submodule add <看板仓库> pages/<id>,清单放在看板仓库根目录,详见仓库内 pages/README.md)。重启 dsh web 即可,不必改宿主代码。
运行时注册(供其它 DSH 插件):
await ctx.subPages.register({ id: 'x', title: 'X', target: 'http://127.0.0.1:9000' })
ctx.subPages.list()
ctx.subPages.unregister('x')
ctx.subPages.mountPath // '/subpages'
下游鉴权(令牌不下发)¶
可选 auth:从 tokenEnv 或 tokenFile + tokenPath 读令牌,以 header/scheme 或 queryParam 注入转发;浏览器侧的 ?token= 会在转发前剥掉。宿主还可代持下游会话 cookie,浏览器既看不到令牌,也拿不到下游 cookie。令牌不会出现在清单 JSON、HTML 或可见 URL 中(仓库含对应测试)。
自带示例看板¶
pages/ 下两个 submodule 分别演示代理 + standalone 与静态 + 独立样式:
| 看板 | 仓库 | 形态 |
|---|---|---|
| 待办 | notify-hub | 代理 |
| 用量 | dsh-usage-stats | 静态单文件 HTML |
可选:启动通告¶
配置 startupNotice 后,DSH 每次启动可把带 token 的访问链接投递到 notify-hub(再转发飞书等)。须显式 enabled: true 并配置 publicHost;token 从启动日志新增横幅读取,未配 publicHost 则不投递。
安装与启用¶
安装命令以 README 为准:
# 本机开发(符号链接,改完即生效)
dsh plugin --profile web add "$PWD" # 在仓库根目录执行
# 从 GitHub 装(插件市场也可直接装)
dsh plugin --profile web add github:SJTUMalPan/dsh-subpages
# 锁 commit(可复现)
dsh plugin --profile web add "git+https://github.com/SJTUMalPan/dsh-subpages#<sha>"
装完重启 dsh web。侧边栏底部会出现「看板」按钮。
卸载:
dsh plugin --profile web remove dsh-subpages
典型用法:挂载一个代理看板¶
在 pages/ 下建目录并放置 subpage.json(字段以 README 表格为准):
{
"id": "notify-hub",
"title": "待办",
"icon": "inbox",
"order": 10,
"target": "http://127.0.0.1:8000",
"health": "/healthz",
"style": "standalone",
"auth": {
"tokenFile": "~/notify-hub-run/config.yaml",
"tokenPath": "server.auth_token",
"queryParam": "token"
}
}
target 必填;id 缺省用目录名,须 kebab-case。下游服务需在本机监听对应端口并已启动,否则门户会显示「看板离线」。
profile 中可改插件 config(或 cordis.patch.yml):
- id: subpages
config:
mountPath: /subpages
pagesDirs: [/abs/extra]
requestTimeoutMs: 15000
probeTimeoutMs: 2000
适用场景与注意¶
适合:已有或计划用独立仓库维护小面板,希望与 DSH Web 同端口、同登录;需要真 URL 深链;希望静态页与门户风格统一且零额外设计系统。
信任模型(挂载前必读):挂一个看板等于让它的前端在 DSH 同源下运行。门户 iframe 使用 allow-same-origin allow-scripts,看板脚本可访问 DSH 的 Cookie、存储与 HTTP 接口,并可操作父窗口 DOM。只挂自己信任、自己审计过的看板;第三方代码应部署在独立源再以跨源方式接入,本插件不做跨源隔离。
进程与权限:插件以当前 DSH 进程权限运行;安装前请阅读源码与 MIT 许可证,并理解 auth、转发头白名单(浏览器 cookie/authorization 不会透传给下游)及「同一看板共用进程级下游会话」的语义。
排障速查:无「看板」按钮 → 确认 profile 已装插件且重启过 dsh web;「还没有挂载任何看板」→ 检查 pages/ 与 subpage.json;链接跳出网关 404 → 下游勿用绝对 URL 重定向;401 → 刷新 DSH 页面重新登录。
结尾¶
dsh-subpages 把「独立小应用」收成 DSH 内的看板:不新开监听端口、清单与 submodule 解耦宿主代码、网关补上前端深链,并可选把启动链接推到 notify-hub。DeepSeek Harness 生态强调「一切皆插件」;社区目录 SkillHub 与 GitHub 仓库并列,便于检索与安装,与 DeepSeek / 幻方无官方从属关系。
- 目录页:https://www.skillhub.cn/plugins/SJTUMalPan/dsh-subpages
- GitHub:https://github.com/SJTUMalPan/dsh-subpages