✨ 提议增强:公网自动开 + Bark 推送,扫码直达 DSH(已实现)
感谢 dsh-pocket —— 出门远程用 DSH 全靠它,体验很好 🐾
最近加了个增强:DSH 启动时自动开公网 + 自动 Bark 推送 → 点推送直接进 DSH。手机端体验从「4 步」变「1 步」,整理一下分享,作者觉得合适可以吸收进主仓库。
🎯 做了什么
- 配置项
autoStartPublic:勾上后 DSH web 启动后自动开公网隧道
- 配置项
push.bark:填 Bark App 设备 key,公网 ready 后自动推 Bark 通知
- 推送带
?token= 魔法链接:点推送 → Edge/Safari → proxy 验证 + 种 cookie → 进 DSH
最终体验:DSH web 启动 → 自动开公网 + 自动推 Bark → iPhone 点推送 → 进 DSH(零手动操作)。
📁 改动清单(9 个文件)
| 文件 |
改动 |
lib/push.mjs |
新增 —— push provider 抽象 + Bark 实现 + 配置加载(~100 行) |
lib/settings.mjs |
加 push + autoStartPublic re-export |
lib/index.js |
apply() 读配置 + onTunnelReady 触发 push + autoStartPublic 钩子 |
lib/service.mjs |
暴露 tunnelUrl getter |
lib/web-rpc.js |
加 4 个 RPC 端点(push.config.set/clear、push.test、autoStartPublic.set)+ status 增 2 字段 |
lib/proxy.mjs |
⚠️ 顺手修一个 bug:?token= 路径下也种 cookie(详见下方) |
client/api.js |
加 4 个端点常量 |
client/index.jsx |
设置页加「🚀 全自动」配置 block |
⚠️ 顺手修的真 bug:公网首访白屏
现象:从外网首次访问公网 URL 带 ?token=<密码> 时,主页 200 但所有 <script src="/assets/*.js"> 和 <link href="/assets/*.css"> 子资源 401,页面只剩空 div → 白屏。
根因:lib/proxy.mjs 的 authCheck() 通过 query 参数验证后直接放行,没种 HttpOnly cookie(种 cookie 只在 POST /pocket-login 路径做了)。浏览器并发请求子资源时,子资源 URL 没 ?token= 也没 cookie → 401。
复现:清空 iPhone 上 trycloudflare 域名 cookie → 访问 https://<公网URL>/?token=<当前密码> → Network 看子资源全 401。
修复(新增 maybeSeedAuthCookie,约 20 行):
function maybeSeedAuthCookie(req, res, token) {
if (!token) return;
if (parseCookies(req.headers.cookie)[TOKEN_COOKIE] === token) return;
const origWriteHead = res.writeHead.bind(res);
res.writeHead = function (statusCode, headers) {
const h = { ...(headers ?? {}) };
h['set-cookie'] = `${TOKEN_COOKIE}=${token}; HttpOnly; SameSite=Lax; Path=/`;
return origWriteHead(statusCode, h);
};
}
主流程 authCheck 通过后调用,WS upgrade 路径不需要(浏览器发 WS 前已经收到 Set-Cookie)。
🔑 关键 diff 片段
lib/index.js —— onTunnelReady 触发 push:
onTunnelReady: () => {
const fresh = rotateAccessToken();
+ const url = service.tunnelUrl;
+ if (url) {
+ const magicUrl = `${url}${url.includes('?') ? '&' : '?'}token=${fresh}`;
+ void sendPocketPush({ url: magicUrl, body: '📶 点此直接进入(密码已自动填好)' })
+ .then((r) => {
+ if (r.ok) logger.info('dsh-pocket: Bark push sent');
+ else logger.warn('dsh-pocket: Bark push failed: %s', r.error);
+ });
+ }
return fresh;
},
lib/index.js —— apply() 加 autoStartPublic 钩子:
void service.startProxy().then((proxy) => {
...
void service.restoreTunnelIfNeeded?.().catch(() => {});
+ if (readAutoStartPublic()) {
+ setTimeout(() => {
+ if (service.tunnelUrl !== null) return;
+ service.startTunnel().catch((err) => {
+ logger.error('dsh-pocket: autoStartPublic failed: %s', err?.message ?? err);
+ });
+ }, 1500);
+ }
})
⚙️ settings.json 形态
{
"lanAuthEnabled": false,
"autoStartPublic": true,
"push": {
"provider": "bark",
"key": "<your-bark-device-key-from-ios-app>"
}
}
(向后兼容:缺 push / autoStartPublic 字段都视为关闭,老用户无感。Bark key 从 iOS Bark App 主界面获取。)
🤔 设计取舍
- 先只做 Bark,provider 抽象留扩展点(
ntfy / webhook 一段 if 即可)—— 国内 iOS 推送 Bark 几乎是事实标准,先解决主要矛盾
- 推送失败静默(fire-and-forget):不阻塞公网隧道本身,下次重启会重推
?token= 复用现有 authCheck:零新接口、零安全妥协
- WS 路径不重复种 cookie:浏览器收 HTML 的 Set-Cookie 后才发 WS
- 不引入新依赖:用 Node 内置
fetch(>=18),无需 node-fetch
- cookie SameSite=Lax:和现有
dsh_pocket_token 一致
🧪 验证步骤
- iPhone 装 Bark → 复制设备 key
- 设置页填 key → 保存 → 测试推送
- 勾上「启动时自动开公网」
- 重启 dsh web → iPhone 应自动收到「📱 DSH 已就绪」推送 → 进 DSH
- 之后再重启 → 又自动推送 → 又自动进
🐾 是否愿意 merge
如果合适可以拆两个 PR:
如果 idea 不错但不想合 Bark(避免单一 provider 绑定),可以做成官方文档 + 第三方脚本形式(独立 bin/dsh-pocket-push.mjs CLI),不动主插件代码。我这边已实现但保留剥离余地。
再次感谢这个项目 ——「把 DSH 装进口袋」对经常出差的开发者真的是刚需喵 🐾
✨ 提议增强:公网自动开 + Bark 推送,扫码直达 DSH(已实现)
感谢 dsh-pocket —— 出门远程用 DSH 全靠它,体验很好 🐾
最近加了个增强:DSH 启动时自动开公网 + 自动 Bark 推送 → 点推送直接进 DSH。手机端体验从「4 步」变「1 步」,整理一下分享,作者觉得合适可以吸收进主仓库。
🎯 做了什么
autoStartPublic:勾上后 DSH web 启动后自动开公网隧道push.bark:填 Bark App 设备 key,公网 ready 后自动推 Bark 通知?token=魔法链接:点推送 → Edge/Safari → proxy 验证 + 种 cookie → 进 DSH最终体验:
DSH web 启动 → 自动开公网 + 自动推 Bark → iPhone 点推送 → 进 DSH(零手动操作)。📁 改动清单(9 个文件)
lib/push.mjslib/settings.mjslib/index.jsapply()读配置 +onTunnelReady触发 push + autoStartPublic 钩子lib/service.mjstunnelUrlgetterlib/web-rpc.jspush.config.set/clear、push.test、autoStartPublic.set)+ status 增 2 字段lib/proxy.mjs?token=路径下也种 cookie(详见下方)client/api.jsclient/index.jsx现象:从外网首次访问公网 URL 带
?token=<密码>时,主页 200 但所有<script src="/assets/*.js">和<link href="/assets/*.css">子资源 401,页面只剩空 div → 白屏。根因:
lib/proxy.mjs的authCheck()通过 query 参数验证后直接放行,没种 HttpOnly cookie(种 cookie 只在 POST/pocket-login路径做了)。浏览器并发请求子资源时,子资源 URL 没?token=也没 cookie → 401。复现:清空 iPhone 上 trycloudflare 域名 cookie → 访问
https://<公网URL>/?token=<当前密码>→ Network 看子资源全 401。修复(新增
maybeSeedAuthCookie,约 20 行):主流程
authCheck通过后调用,WS upgrade 路径不需要(浏览器发 WS 前已经收到 Set-Cookie)。🔑 关键 diff 片段
lib/index.js——onTunnelReady触发 push:onTunnelReady: () => { const fresh = rotateAccessToken(); + const url = service.tunnelUrl; + if (url) { + const magicUrl = `${url}${url.includes('?') ? '&' : '?'}token=${fresh}`; + void sendPocketPush({ url: magicUrl, body: '📶 点此直接进入(密码已自动填好)' }) + .then((r) => { + if (r.ok) logger.info('dsh-pocket: Bark push sent'); + else logger.warn('dsh-pocket: Bark push failed: %s', r.error); + }); + } return fresh; },lib/index.js——apply()加 autoStartPublic 钩子:void service.startProxy().then((proxy) => { ... void service.restoreTunnelIfNeeded?.().catch(() => {}); + if (readAutoStartPublic()) { + setTimeout(() => { + if (service.tunnelUrl !== null) return; + service.startTunnel().catch((err) => { + logger.error('dsh-pocket: autoStartPublic failed: %s', err?.message ?? err); + }); + }, 1500); + } })⚙️ settings.json 形态
{ "lanAuthEnabled": false, "autoStartPublic": true, "push": { "provider": "bark", "key": "<your-bark-device-key-from-ios-app>" } }(向后兼容:缺
push/autoStartPublic字段都视为关闭,老用户无感。Bark key 从 iOS Bark App 主界面获取。)🤔 设计取舍
ntfy/webhook一段 if 即可)—— 国内 iOS 推送 Bark 几乎是事实标准,先解决主要矛盾?token=复用现有authCheck:零新接口、零安全妥协fetch(>=18),无需node-fetchdsh_pocket_token一致🧪 验证步骤
🐾 是否愿意 merge
如果合适可以拆两个 PR:
maybeSeedAuthCookie)—— 强烈建议合,影响所有公网首访如果 idea 不错但不想合 Bark(避免单一 provider 绑定),可以做成官方文档 + 第三方脚本形式(独立
bin/dsh-pocket-push.mjsCLI),不动主插件代码。我这边已实现但保留剥离余地。再次感谢这个项目 ——「把 DSH 装进口袋」对经常出差的开发者真的是刚需喵 🐾