ced05e06a3
用独立反向代理把 dsh Web UI 发布到局域网。官方 CLI 主动拒绝 `dsh web --host 0.0.0.0`(会把宿主机远程代码执行暴露到网络), 因此本插件让官方服务器保持只绑回环,另开监听并转发。 宿主端(index.js) - 在 0.0.0.0:3082 监听,把 HTTP 与 WebSocket 转发到 127.0.0.1:<webServer 端口> - 上游 Host/Origin 重写为回环,使官方 /api 信任围栏「通过」而不是被绕过 - 不改动 Set-Cookie:浏览器按请求 URL 建立 host-only cookie,转发层无需干预 - 监听端 DNS 重绑定防护;targetHost 仅接受回环地址(不会变成开放代理) - /api/dsh-lan-access/summary 复用官方 connection.admit() 做围栏与浏览器认证 - 启动横幅打印带 launch token 的局域网链接 - 端口被占用时只告警不抛出,并在面板上报 listening:false,不展示不可用的二维码 - ownsHostCompat(默认关):仅为非回环页面声明 ownsHost,恢复官方设置界面 客户端(src/ 经 scripts/build-client.mjs 生成 client.js) - Settings → 局域网访问:局域网地址列表、复制/打开、选中地址的二维码 - 零依赖二维码编码器(byte 模式 / ECC M / version 1–10) 组合层 - 只 insert 一行,不覆盖任何 shipped row:停用本插件只会移除局域网监听, 回环 Web UI 不受影响 验证 - 宿主转发契约 28/28;客户端契约 25/25 - 二维码编码器与 npm qrcode 参考实现在全版本 × 全 8 掩码下逐模块比对 240/240 一致 - 端到端:局域网 token 首访 303 并铸 cookie → 应用 200;无凭证 401; WebSocket 升级 101,且与直连回环逐项行为一致
61 lines
2.1 KiB
JavaScript
61 lines
2.1 KiB
JavaScript
/**
|
|
* Emit the single browser artifact `client.js` from `src/panel.js` and
|
|
* `src/qrcode.js`.
|
|
*
|
|
* A bundle's client half is served as one self-contained module; it cannot
|
|
* import a sibling file or a package dependency at runtime. This script inlines
|
|
* both sources into the `window.__ModuleLoader__.load` shell, which is the same
|
|
* shape the reference plugins ship.
|
|
*
|
|
* Run: `node scripts/build-client.mjs`
|
|
*/
|
|
|
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
import { dirname, join } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
const root = join(here, '..')
|
|
|
|
/** Read a source file and turn its ESM exports into plain declarations. */
|
|
function inline(relativePath) {
|
|
const source = readFileSync(join(root, relativePath), 'utf8')
|
|
return source
|
|
.replace(/^import .*$/gm, '')
|
|
.replace(/^export (?=(?:function|const|let|class)\b)/gm, '')
|
|
.trim()
|
|
}
|
|
|
|
const qrcode = inline('src/qrcode.js')
|
|
const panel = inline('src/panel.js')
|
|
|
|
const artifact = `/**
|
|
* @sutong/dsh-lan-access — browser half (generated by scripts/build-client.mjs).
|
|
*
|
|
* Do not edit this file: change src/panel.js or src/qrcode.js and rebuild.
|
|
*/
|
|
window.__ModuleLoader__.load({
|
|
id: "@sutong/dsh-lan-access",
|
|
factory: function (require) {
|
|
"use strict";
|
|
var React = require("react");
|
|
|
|
// ── inlined src/qrcode.js ────────────────────────────────────────────────
|
|
var __qrcode = (function () {
|
|
${qrcode.replace(/^/gm, ' ')}
|
|
return { encodeQR: encodeQR, MAX_VERSION: MAX_VERSION };
|
|
})();
|
|
var encodeQR = __qrcode.encodeQR;
|
|
|
|
// ── inlined src/panel.js ─────────────────────────────────────────────────
|
|
${panel.replace(/^/gm, ' ')}
|
|
|
|
return { inject: inject, apply: apply };
|
|
},
|
|
});
|
|
`
|
|
|
|
const out = join(root, 'client.js')
|
|
writeFileSync(out, artifact)
|
|
console.log(`wrote ${out} (${artifact.length} bytes)`)
|