# dsh-lan-proxy 架构与运行机制(TOGAF 4A 四视图) > 包:`@wingsky-1/dsh-lan-proxy` · 源码:`packages/dsh-lan-proxy/` · 版本:0.2.4 > 功能一句话:**局域网访问 dsh web UI**——在 `0.0.0.0:` 监听,把 HTTP/HTTPS 与 > WebSocket/wss 转发到回环 web 服务器(默认 `127.0.0.1:3080`),并附带 DNS 重绑定防护、 > HTTPS 并存、WS 压缩桥接、HTTP 响应压缩与 launch-token 自动注入。 > > 快速上手(安装 / 配置键 / 验证命令)见 [包 README](../../packages/dsh-lan-proxy/README.md);本文讲**原理与运行机制**。 --- ## 四视图导航 | 视图 | 回答的问题 | 图 | |---|---|---| | [BA 业务架构](#user-content-ba) | 谁使用、得到什么、不提供什么 | [BA 图](diagrams/lan-proxy-ba.svg) | | [AA 应用架构](#user-content-aa) | 装配、功能域与客户端如何协作 | [AA 图](diagrams/lan-proxy-aa.svg) | | [DA 数据架构](#user-content-da) | 配置、证书、认证材料与状态由谁持有 | [DA 图](diagrams/lan-proxy-da.svg) | | [TA 技术架构](#user-content-ta) | 挂载、构建、信任边界与兼容性 | [TA 图](diagrams/lan-proxy-ta.svg) | > 各视图的 SVG 由同名 HTML 导出;正文 Mermaid 讲关键链路。证据为 `路径:行号`(取自 > 80a8584a 树,后续提交会漂移,以符号搜索兜底)或可复现常量;不把历史性能读数当本次验证。 > 路径默认相对 `packages/dsh-lan-proxy/`。 ## 1. 业务架构(BA) ![BA:局域网访问能力与用户可见结果](diagrams/lan-proxy-ba.svg) ### 1.1 使用者、能力与非目标 - 局域网设备访问同一台宿主的 dsh web,不创建第二套会话服务。 - 管理者经设置卡片调整监听端口、证书、压缩和访问策略;配置观察器驱动监听器重建。 - TLS 和压缩改善传输条件,不提供用户隔离;插件不是任意上游的通用代理,也不替代宿主认证。 - `injectToken` 默认开启会扩大访问授权;`ownsHostCompat` 默认关闭,仅影响页面 Host trust 声明,两者不是同一开关。 证据:`src/server/apply.ts:72`(apply)、`src/server/config/impl/model.ts:132`(`wsBridgeEnabled` 默认 true)、`:173`(`ownsHostCompat` 默认 false)、`src/client/settings-card.tsx`。 ### 1.2 总体访问链 ```text 局域网设备 ──HTTP/HTTPS/WS──▶ lan-proxy 转发器 ──回环转发──▶ dsh web (Node 进程内) (127.0.0.1:3080) ``` 原单图 `lan-proxy-architecture.{html,svg}` 保留为历史图源,当前结构以本页四视图为准。 关键设计取舍: - **转发器是独立的监听层**,不改 dsh web 一行代码——经 `cordis.patch.yml` 挂载到 profile 插件名册,宿主端 `apply(ctx)` 在回环 web 服务器绑定后启动,从 `ctx.webServer.port` 解析真实上游端口(`--port 0` 也适用); - **两类流量两种处理**:普通 HTTP(S) 走 http-proxy 转发 + 响应压缩;默认所有 WS 都终结后桥接以保活,只有压缩开关开启、白名单命中且 UA 策略允许时才协商压缩; - **三条安全防线**:① `targetHost` 仅允许回环(防开放转发/SSRF);② 入站 Host 仅接受 IP 字面量或 `localhost`(DNS 重绑定防护);③ 配置/health 路由走 `shared/loopback.js` 的 loopback 围栏。 --- ## 2. 应用架构(AA) ![AA:装配层、功能域与客户端](diagrams/lan-proxy-aa.svg) ### 2.1 装配边界与域分工 `src/index.ts` 是包导出契约,实际组合根在 `src/server/apply.ts`。不要套用 notifier 的 八域单例模板:这里以装配闭包持有配置来源、监听器和定时器,各域经 `interface.ts` 供调用。 | 域 | 职责与依赖 | 主要证据 | |---|---|---| | config | schema、读写路由、官方 settings 接线;消费 shared 默认值 | `config/impl/model.ts:225`(`FILE_CONFIG_VALIDATORS`)、`config/impl/namespace.ts:18`(`SETTINGS_NS`)、`:28`(watch) | | migrate | 旧格式迁移;消费 config 的净化与写端口 | `migrate/impl/file/index.ts:16`(`MIGRATED_BAK_NAME`) | | tls | 加载成对证书或生成自签名材料,交给装配层 | `tls/impl/index.ts:50`(loadTlsFromFiles)、`:79`(ensureSelfSignedTls) | | proxy | 独立 HTTP/HTTPS 监听、转发、压缩、WS 桥接 | `proxy/impl/proxy.ts:671`(createLanProxy) | | host-trust | 经官方 tapIndex 注入自条件脚本;直接消费窄用途 ctx | `host-trust/impl/injection.ts:79`(registerHostTrustInjection) | `src/server/shared/` 是默认值与回环目标判据的共享叶子。浏览器端由 `src/client/index.ts` 装配样式、locale、设置卡片与独立控制台观测; `settings-card.tsx` 负责同源配置请求,`host-trust-status.ts` 负责信号判定。 ### 2.2 装配与卸载时序 `apply(ctx)` 在宿主启动时被调用,按固定顺序装配(`src/server/apply.ts`,同步返回,转发器在 `sync()` 内立即启动): ```mermaid flowchart TD S(["dsh web 启动
cordis.patch.yml 挂载 ⇢ apply(ctx)"]) --> A["插件目录准备
DSH_HOME/lan-proxy"] A --> B["闭包状态初始化
config current() / disposeProxy"] B --> C["resolve(): 补全配置默认值
wsCompressPaths 归一化"] C --> D["ctx.inject(connection)
launch token 提供者接线 (injectToken)"] D --> E["compressSnapshot()
压缩运行快照(health 共用)"] E --> F{"sync:先拆旧;enabled 时准备 TLS"} F -->|"用户配 tlsCertFile/tlsKeyFile"| G["loadTlsFromFiles"] F -->|"未配置"| H["ensureSelfSignedTls
selfsigned rsa:2048 · SHA256 · 825 天"] G --> I H --> I["sync(): 拆旧 → 建转发器 → listen()
成功打印横幅 · EADDRINUSE 关闭清理"] I --> L["注册两条 tapIndex
UUID polyfill + 自条件 host trust 注入"] L --> M["installLanProxySettings
官方 settings 命名空间接线
+ 存量 config.json 一次性迁移"] M --> N["注册 2 条 loopback 路由:
GET/PUT /api/dsh-lan-proxy/config
GET /api/dsh-lan-proxy/health"] N --> O["ctx.effect 生命周期 disposer"] M -.->|"watch:防抖 3s 后重建"| F O -->|"卸载"| CLEAN["清定时器、撤路由与 tap、关闭转发器"] ``` 要点: - **配置三通道**:官方 settings 命名空间 `dsh-lan-proxy`(`config/impl/namespace.ts:18`,user 层) =权威持久层 → 组合层 cordis config(base 层)→ schema 默认值兜底;解析顺序 `defaults → base → user`。`scope.watch` 驱动 `scheduleSync`(`apply.ts:333-340`,**3000ms** 防抖,`:339`),无需重启。防抖是刻意取舍:重建会 dispose 当前转发器、掐断经 lan-proxy 正访问设置页的连接,先让保存回执发出再重建(`apply.ts:328-331` 注释明言「过早重建会丢失 HTTP 响应(保存误报失败)」); - **存量 `/lan-proxy/config.json`** 在 settings attach 后由 `migrateFileConfig` 迁移进官方存储(`apply.ts:395-401` onScope 内,**前置于一切 enabled 判定**,禁用用户升级同样迁移); 原文件改名 `.migrated.bak`,bak 重放行为见 §3.2,不再把旧 config.json 当作配置权威源; - **随机 UUID polyfill**:LAN 明文 HTTP 是非安全上下文,缺 `crypto.randomUUID`, 否则客户端 RPC 的 `mintRpcId` 全抛错——`apply.ts:349-373` 经 `webServer.tapIndex` 以 `