前言¶
在 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