dsh-mobile 0.6.17 → 0.6.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,188 @@
1
+ # 移动端插件适配指南
2
+
3
+ [English](MOBILE_PLUGIN_ADAPTATION.en.md) · [返回文档索引](README.md)
4
+
5
+ 这份指南面向 DSH 插件作者,目标是**小屏可用,桌面功能不回退**。DSH Mobile 提供连接、页面布局和公共入口,不会逐个重排第三方插件的内部控件。可以先用浏览器完成适配,再补充真机验证;没有 Android 设备并不妨碍开始。
6
+
7
+ ## 先确认面板所在位置
8
+
9
+ | 当前移动页面行为 | 插件应如何处理 |
10
+ | --- | --- |
11
+ | 打开 `main` 槽中的面板时,布局在面板上方提供「返回会话」栏 | 使用容器实际可用高度;该栏至少 56px,安全区和内容可能增加高度,不要手动从屏幕高度固定减去 56px |
12
+ | 窄屏侧栏以抽屉显示,展开宽度不超过 `min(88vw, 340px)`;宽屏可以常驻显示 | 不假设侧栏永远是 340px,入口文字和按钮应能收缩 |
13
+ | 移动布局带有 `.dshm-shell` 和 `[data-pane="conversation"]`、`[data-pane="sidebar"]` 标记 | 排障时可用这些标记定位区域;插件内部样式优先作用于自己的根节点,不依赖宿主私有 CSS 类名 |
14
+ | 移动外壳为输入框和可编辑区域设置至少 16px 的字体,同时保留更大的继承字号 | 为输入区域预留高度,不用更小的固定字体抵消用户设置 |
15
+ | 公共入口的适配不会保证任意第三方控件的可点击区域 | 插件作者仍需检查按钮、关闭键、复选框和菜单的实际命中区域 |
16
+
17
+ 这些是当前布局行为,不是要求插件复制的固定尺寸。浮层、侧栏和主面板的可用空间不同,需在实际挂载位置验证。
18
+
19
+ ## 按可用空间适配
20
+
21
+ 先让基础布局能够收缩:给 flex/grid 子项设置 `min-width: 0`,允许工具栏换行,使用内容驱动的高度。面板宽度不一定等于视口宽度:桌面侧栏中的 400px 面板也需要紧凑布局。
22
+
23
+ 只改变排列时,可用 CSS 容器查询;全屏页面的视口适配仍可使用媒体查询。下面的 `600px` 是示例断点,应按插件内容选择:
24
+
25
+ ```css
26
+ .plugin-panel-host { container-type: inline-size; min-width: 0; }
27
+ .plugin-panel { min-width: 0; }
28
+ .plugin-panel .toolbar { display: flex; flex-wrap: wrap; gap: 8px; }
29
+
30
+ @container (max-width: 600px) {
31
+ .plugin-panel .toolbar { display: grid; grid-template-columns: minmax(0, 1fr); }
32
+ }
33
+ ```
34
+
35
+ `.plugin-panel-host` 应包住 `.plugin-panel`。对不支持容器查询的引擎,基础的换行和流式宽度仍应可用,不要把必需操作藏在唯一的查询分支里。
36
+
37
+ 确实需要脚本分支时,可以在组件内部观察根节点宽度,而不是判断手机 UA:
38
+
39
+ ```js
40
+ const rootRef = useRef(null)
41
+ const [panelWidth, setPanelWidth] = useState(null)
42
+ const isNarrow = panelWidth !== null && panelWidth <= 600
43
+
44
+ useEffect(() => {
45
+ const element = rootRef.current
46
+ if (element === null) return undefined
47
+ const measure = () => setPanelWidth(element.getBoundingClientRect().width)
48
+ measure()
49
+ window.addEventListener('resize', measure)
50
+ const observer = typeof window.ResizeObserver === 'function' ? new window.ResizeObserver(measure) : null
51
+ observer?.observe(element)
52
+ return () => {
53
+ window.removeEventListener('resize', measure)
54
+ observer?.disconnect()
55
+ }
56
+ }, [])
57
+
58
+ // 组件返回值中的根节点;初次测量前也应保留可用的基础布局。
59
+ h('section', { ref: rootRef, className: 'plugin-panel', 'data-narrow': isNarrow ? 'true' : 'false' }, contents)
60
+ ```
61
+
62
+ 没有 ResizeObserver 的回退只能在窗口变化时重测;如果面板会独立调整宽度,还需接入插件已有的布局通知。观察器和监听器应随组件卸载清理。
63
+
64
+ ## 适配清单
65
+
66
+ ### 触控目标与间距
67
+
68
+ 本项目建议主要 Web 操作提供至少 **44×44 CSS px** 的命中区域,主操作有空间时可用 48px;这不是 Android dp 或 iOS pt 的换算。相邻操作保留间距,不通过覆盖邻居来扩大点击区。
69
+
70
+ ```css
71
+ .plugin-panel :is(button, select, input:not([type="checkbox"]):not([type="radio"])) { min-height: 44px; }
72
+ .plugin-panel .icon-button { min-width: 44px; }
73
+ .plugin-panel .actions { display: flex; flex-wrap: wrap; gap: 8px; }
74
+ .plugin-panel .check-label { box-sizing: border-box; display: inline-flex; align-items: center; gap: 8px; min-width: 44px; min-height: 44px; padding-inline: 8px; }
75
+ ```
76
+
77
+ 小复选框可放在关联的 `label` 中,扩大标签的可点击区域,不必放大图标。用实际点按和 `elementFromPoint()` 检查边缘命中;声明了 `min-height` 仍可能被布局压缩、裁切或遮挡。
78
+
79
+ ### 筛选条、标签与行内动作
80
+
81
+ 过长的筛选条可以换行,或放入能横向滚动的独立容器;不要让页面整体横向溢出。保留焦点可见性和足够的内边距,使最后一项也能滚入视野:
82
+
83
+ ```css
84
+ .plugin-panel .chips { display: flex; gap: 8px; overflow-x: auto; padding: 4px; }
85
+ .plugin-panel .chip { flex: 0 0 auto; min-height: 44px; }
86
+ ```
87
+
88
+ 有明确含义的行内动作可以在窄屏收成图标,但要保留可访问名称,必要时在菜单中保留文字说明。不能靠悬停提示作为手机上唯一的操作说明。
89
+
90
+ ```js
91
+ h('button', { className: 'icon-button', 'aria-label': label }, h(Icon, { 'aria-hidden': true, name: 'play' }))
92
+ ```
93
+
94
+ 窄屏不代表没有键盘。可以收起装饰性的快捷键角标,但保留实际快捷键、帮助入口、Tab 顺序和可见焦点;不要只按宽度移除键盘可用功能。
95
+
96
+ ### 字体、密度与完整信息
97
+
98
+ 继承 DSH 的字体和语义颜色;需要跟随移动页面字号时,可在自己的内容根节点使用 `--dsh-content-font-size`,未提供该变量时保留继承值:
99
+
100
+ ```css
101
+ .plugin-panel { font-size: var(--dsh-content-font-size, inherit); line-height: 1.5; }
102
+ .plugin-panel .meta { color: var(--dsw-alias-label-secondary, currentColor); }
103
+ ```
104
+
105
+ 压缩装饰性标题和空白,不压缩触摸目标。长标题允许换行或提供可访问的完整查看入口;错误、待确认内容和关键操作名称不能只剩截断文本。大字号、长路径与中英文内容都要检查。
106
+
107
+ ### 弹层、表单与滚动边界
108
+
109
+ 浮层应受实际容器和可视区约束,避免固定宽高把按钮推到屏幕外。先使用 DSH 的现有浮层组件;自有弹层需保留可达的关闭入口、焦点管理和 Escape 行为。
110
+
111
+ ```css
112
+ .plugin-panel .form-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; }
113
+ .plugin-panel .scroll-body { min-height: 0; overflow: auto; overscroll-behavior: contain; }
114
+ .plugin-panel textarea { box-sizing: border-box; width: 100%; min-height: 96px; }
115
+ ```
116
+
117
+ 只有确实延伸到屏幕边缘且未被宿主处理的区域才追加 `env(safe-area-inset-*)`,避免重复留白。键盘打开后,保存、取消和错误提示仍应可见或可滚动到达;独立滚动区域不应拖动背后的页面。
118
+
119
+ ## 验证:代码检查、浏览器与真机分别证明什么
120
+
121
+ ### 结构检查
122
+
123
+ 单测可验证窄屏分支、可访问名称、状态保留和卸载清理。服务端渲染不会执行浏览器布局测量;给测试提供宽度或布局桩,只能证明选中了哪个分支,不能证明真实尺寸、可点击性或桌面像素一致。不要把 CSS 中出现 `44px` 当成触摸验收。
124
+
125
+ ### 真实浏览器检查
126
+
127
+ 将插件挂到实际 DSH 页面后,至少检查 375px、390px 窄屏、横屏、桌面全宽和桌面窄侧栏。若维护静态预览,还要在浏览器中加载它,并明确它与真实宿主的差异。
128
+
129
+ - 覆盖列表、详情、设置、空状态、长内容和出错状态;
130
+ - 检查元素边界、裁切祖先和实际命中,确认首尾操作都能触达;
131
+ - 用键盘验证 Tab、Escape、焦点恢复,以及浮层打开时背景不会误操作;
132
+ - 改变宽度和字号,确认输入草稿、选择与展开状态保留;
133
+ - 检查浅色、深色和桌面基线;截图差异需解释,不要求有意改进也逐像素不变。
134
+
135
+ 例如,在 Playwright 中对已有面板入口执行实际操作后,可检查控件几何和命中;以下选择器应替换为插件自己的稳定标记:
136
+
137
+ ```js
138
+ const action = page.locator('[data-plugin-action="save"]')
139
+ await action.scrollIntoViewIfNeeded()
140
+ const box = await action.boundingBox()
141
+ if (box === null || box.width < 44 || box.height < 44) throw new Error('Save target is too small')
142
+ const hit = await action.evaluate(element => {
143
+ const box = element.getBoundingClientRect()
144
+ return element.contains(document.elementFromPoint(box.x + box.width / 2, box.y + box.height / 2))
145
+ })
146
+ if (!hit) throw new Error('Save target is obscured')
147
+ ```
148
+
149
+ 中心点命中只是起点,不能替代边缘点按、滚动、焦点与状态检查。具体启动和验证流程见[贡献指南](../CONTRIBUTING.md#select-behavior-checks)。
150
+
151
+ ### 真机补充
152
+
153
+ 浏览器模拟不能代替真实 WebView、输入法、安全区、惯性滚动或外接键盘验收。记录设备、App/WebView 版本、连接方式和实际步骤;没有验证的部分直接说明,不宣称已全平台通过。
154
+
155
+ ## 给插件作者的模板
156
+
157
+ ### Issue
158
+
159
+ ```markdown
160
+ ## 插件与小屏
161
+ - 插件名和版本:
162
+ - DSH、DSH Mobile 与 App/浏览器版本:
163
+ - 面板位置与可用宽度(主面板 / 侧栏 / 浮层):
164
+ - 复现步骤、期望与实际表现:
165
+ - 截图及可定位的控件(隐藏凭据和私人内容):
166
+ - 关闭其他可选前端模块后的对照结果(如已测试):
167
+
168
+ ## 验收目标
169
+ - [ ] 必需操作可见、可点,长内容不被截断
170
+ - [ ] 触控区域、相邻间距和首尾项经过实际检查
171
+ - [ ] 键盘焦点、返回/关闭和草稿状态无回退
172
+ - [ ] 弹层与表单在窄屏和大字号下可用
173
+ - [ ] 桌面主要流程无回退;注明尚未验证的设备行为
174
+ ```
175
+
176
+ ### PR
177
+
178
+ - [ ] 说明受影响入口和内容,不按 UA 硬分手机与桌面;
179
+ - [ ] 优先复用组件与状态;确需不同排列时避免复制业务逻辑;
180
+ - [ ] 给出真实浏览器的窄屏和桌面对照、操作步骤及相关测试;
181
+ - [ ] 检查焦点、命中、长内容、错误和清理,不仅检查 CSS 文本;
182
+ - [ ] 说明真机已验证与未验证的范围,不复制其他版本的结果。
183
+
184
+ ## 范围
185
+
186
+ 本指南来自社区插件适配实践与 [#279 讨论](https://github.com/saya-ch/dsh-mobile/discussions/279),提供可复用的实现与验收思路,不代表这里已独立验证任何第三方插件的新版本。主题细节还可参考[皮肤与移动布局](SKIN_COMPATIBILITY.md)。
187
+
188
+ DSH Mobile 不按第三方私有类名维护通用覆盖表,也不保证依赖原生窗口的插件能在远程页面直接使用。适配应由拥有该组件的插件维护,并使用 DSH 的公开扩展接口;发现宿主缺失的能力时应单独报告,不修改已安装的 DSH 产物来绕过问题。
package/docs/README.md CHANGED
@@ -29,6 +29,14 @@
29
29
  | 为移动页面选择加载模块 | [模块设置](CLIENT_MODULES.md) | [Module settings](CLIENT_MODULES.en.md) | 电脑默认与设备覆盖分别保存,不自动刷新会话 |
30
30
  | 允许受控路由网络访问 LAN Gateway | [额外可信网段](TRUSTED_NETWORKS.md) | [Trusted networks](TRUSTED_NETWORKS.en.md) | 不建立 VPN,不自动扩大防火墙规则 |
31
31
 
32
+ ## 插件移动适配 / Plugin mobile adaptation
33
+
34
+ 给插件作者的响应式布局、触控与验收建议:优先复用现有组件,兼顾小屏与桌面,并区分结构检查、真实浏览器和真机证据。
35
+
36
+ | 主题 | 中文 | English |
37
+ | --- | --- | --- |
38
+ | 让第三方插件面板在手机上可用 | [移动端插件适配指南](MOBILE_PLUGIN_ADAPTATION.md) | [Mobile plugin adaptation](MOBILE_PLUGIN_ADAPTATION.en.md) |
39
+
32
40
  ## 开发与安全 / Development and security
33
41
 
34
42
  0.6.3 的 [扩展 Worker 指南](EXTENSION_WORKERS.md)/[Worker guide](EXTENSION_WORKERS.en.md) 介绍按扩展启用、预算与显式恢复;此功能不迁移普通 Cordis 插件,也不是权限沙箱。
@@ -47,7 +47,7 @@ Select **Save and start backend**. An active backend reloads its settings. The p
47
47
  2. Point the proxy at the displayed private HTTP backend, not the phone's public HTTPS address.
48
48
  3. Preserve the external **Host including non-default port**: the example requires `phone.example.com:8815`, not the private target or a hostname without its port.
49
49
  4. Preserve Origin, Cookie, Set-Cookie and authentication/CSRF headers. Do not rewrite Cookie domain/security attributes or cache authentication responses.
50
- 5. Forward HTTP/1.1 WebSocket upgrades and bidirectional data. Approve any required third-party WebSocket paths separately in diagnostics.
50
+ 5. Forward HTTP/1.1 WebSocket upgrades and bidirectional data. The existing WebSocket path allowlist keeps applying; when a third-party plugin needs its own path, approve it separately in diagnostics.
51
51
 
52
52
  [Lucky's official Web documentation](https://www.lucky666.cn/docs/modules/web/) describes WebSocket support. UI labels vary by version, but this backend requires the incoming external Host. Do not enable target-address Host; if a version offers custom Host, provide the full public hostname and port. Certificates belong on Lucky, not this HTTP backend.
53
53
 
@@ -48,7 +48,7 @@ CIDR 限制针对 TCP 连接的**直接来源地址**,不是手机 IP,也不
48
48
  2. 代理目标使用面板给出的 **HTTP 后端地址**,而不是手机使用的 HTTPS 地址。
49
49
  3. **保留请求的外部 Host,包含非默认端口。** 上例后端必须收到 `Host: phone.example.com:8815`,不能改成 `192.168.50.10:3444`,也不能漏掉 `:8815`。
50
50
  4. 透传浏览器的 Origin、Cookie、Set-Cookie,以及认证/CSRF 相关头,不要改写 Cookie 的域或安全属性;不要缓存配对或认证响应。
51
- 5. 透传 WebSocket 升级(HTTP/1.1、Upgrade、Connection)以及双向数据。已有的 WebSocket 路径白名单继续生效;第三方插件需要时仍在现有面板中单独允许其路径。
51
+ 5. 透传 WebSocket 升级(HTTP/1.1、Upgrade、Connection)以及双向数据。已有的 WebSocket 路径白名单继续生效;第三方插件需要时仍在**连接诊断**面板中单独允许其路径。
52
52
 
53
53
  [Lucky 官方 Web 文档](https://www.lucky666.cn/docs/modules/web/)说明其支持 WebSocket。配置界面随版本变化,但本插件要求不变:保留外部请求 Host,不要启用“使用目标地址Host”;若版本提供自定义 Host,请填写完整公网主机名及端口。TLS/证书配置在 Lucky,不在这个 HTTP 后端上配置。
54
54
 
@@ -86,7 +86,7 @@ location / {
86
86
 
87
87
  **“后端已监听”只证明本地 HTTP 监听成功,不代表公网 HTTPS、证书或 WebSocket 已成功。** 自备代理模式的连接诊断也只报告本地状态,不自动向公网地址探测。
88
88
 
89
- 在电脑端点击 **生成远程配对二维码**,再在 Android App **0.4.0 或更高版本**的 **远程访问** 流程扫码(不要使用 LAN 扫码流程)。现场验证中 0.3.16 会拒绝自有域名配对;现有自有域名逻辑保留 HTTPS 自定义端口,无需为此功能修改 APK。
89
+ 在电脑端点击 **生成远程配对二维码**,再在 Android App **0.4.0 或更高版本**的 **远程访问** 流程扫码(不要使用 LAN 扫码流程)。使用受信任证书的自有域名也可在手机浏览器中完成配对;现有自有域名逻辑保留 HTTPS 自定义端口,无需为此功能修改 APK。
90
90
 
91
91
  上线前,必须用手机从目标外网实际检查:
92
92
 
@@ -8,7 +8,7 @@ Start with the actual channel rather than treating every delay as a DSH heartbea
8
8
 
9
9
  ## Identify the stage that fails
10
10
 
11
- First record the DSH, DSH Mobile plugin and Android app versions. On the computer, check the current channel under **Mobile Access → Connection diagnostics**, then inspect DSH startup logs and the browser's developer tools under Network → WS and update the component involved. The plugin and app release independently; their version numbers do not need to match. Remove Tokens, Cookies, and complete pairing URLs before sharing logs.
11
+ First record the DSH, DSH Mobile plugin and Android app versions. On the computer, check the current channel under **Mobile Access → Diagnostics**, then inspect DSH startup logs and the browser's developer tools under Network → WS and update the component involved. The plugin and app release independently; their version numbers do not need to match. Remove Tokens, Cookies, and complete pairing URLs before sharing logs.
12
12
 
13
13
  - **WebSocket never connects**: check for `101 Switching Protocols` and errors such as `401`, `403`, `404`, or `502`. Authentication, Host/Origin checks, proxy upgrade support, paths, and version differences can all cause this. Heartbeat adjustments do not resolve these failures. Allow blocked third-party plugin paths individually in Connection diagnostics; DSH's built-in paths do not need to be added again.
14
14
  - **The request gets 101 but connections keep closing**: record the interval and compare the same page and Session through LAN and remote access. If a long Session repeatedly disconnects only through the remote channel while downloads saturate the link, investigate the heartbeat and initialization deadlines below. Browser close code `1006` means an abnormal end and does not identify which layer disconnected.
@@ -18,7 +18,7 @@ Both [#126](https://github.com/saya-ch/dsh-mobile/issues/126) and [#146](https:/
18
18
 
19
19
  ### HTTP rejection diagnostics (plugin 0.6.11+)
20
20
 
21
- Plugin 0.6.11 and later write limited `auth failure` HTTP diagnostics to the plugin log (`$DSH_HOME/mobile-access/logs/dsh-mobile.log`): authorization versus CSRF rejection, status, method, a safe route or route category, related Cookie/header presence and counts, normalized Origin and recognized fetch-site values. It omits tokens, Cookie values, query strings and arbitrary original paths. Each Gateway rate-limits its two categories separately: the first event is immediate and same-category rejections aggregate within a 30-second window; `suppressed` counts previously suppressed records of that category, not all requests or only the current path. This helps investigate missing credentials and CSRF failures; it does not automatically repair a 401/403. The absence of a record does not establish successful authentication: earlier Host/Origin rejections, WebSocket upgrades and native pairing/renewal are outside these two recording points. Review logs before sharing them.
21
+ Plugin 0.6.11 and later write limited `auth failure` HTTP diagnostics to the plugin log (`$DSH_HOME/mobile-access/logs/dsh-mobile.log`): authorization versus CSRF rejection, status, method, a safe route or route category, related Cookie/header presence and counts, normalized Origin, recognized fetch-site values and, when authorization is present, the first 8 characters of the device id. It omits tokens, Cookie values, query strings and arbitrary original paths. Each Gateway rate-limits its two categories separately: the first event is immediate and same-category rejections aggregate within a 30-second window; `suppressed` counts previously suppressed records of that category, not all requests or only the current path. This helps investigate missing credentials and CSRF failures; it does not automatically repair a 401/403. The absence of a record does not establish successful authentication: earlier Host/Origin rejections, WebSocket upgrades and native pairing/renewal are outside these two recording points. Review logs before sharing them.
22
22
 
23
23
  ### Internal authentication exchange diagnostics (plugin 0.6.15+)
24
24
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## 先判断重连发生在哪一步
10
10
 
11
- 先记录 DSH、DSH Mobile 插件和 Android App 的版本,再在电脑端 **移动访问 → 连接诊断** 检查当前通道;查看 DSH 启动日志和浏览器开发者工具的 Network → WS,按问题涉及的组件更新。插件与 App 独立发版,版本号不必相同。分享日志时去掉 Token、Cookie 和完整配对地址。
11
+ 先记录 DSH、DSH Mobile 插件和 Android App 的版本,再在电脑端 **移动访问 → 诊断** 检查当前通道;查看 DSH 启动日志和浏览器开发者工具的 Network → WS,按问题涉及的组件更新。插件与 App 独立发版,版本号不必相同。分享日志时去掉 Token、Cookie 和完整配对地址。
12
12
 
13
13
  - **WebSocket 没有连接成功**:检查请求是否获得 `101 Switching Protocols`,并留意 `401`、`403`、`404`、`502` 等错误。认证、Host/Origin、反向代理升级支持、连接路径或版本差异都可能造成这种情况。调整心跳不能解决这些问题;第三方插件被拦截的路径可在连接诊断中按需允许,不必把 DSH 内置路径全部重新添加。
14
14
  - **已获得 101,但连接反复关闭**:记录关闭间隔,并对比同一页面、同一会话的局域网与远程访问。如果只在远程打开长会话时频繁断开,同时下载长时间占满链路,才进一步检查下面的心跳和初始化等待时间。浏览器显示 `1006` 表示连接异常结束,本身不能确定是哪一层断开。
@@ -18,7 +18,7 @@
18
18
 
19
19
  ### HTTP 拒绝诊断(插件 0.6.11 起)
20
20
 
21
- 插件 0.6.11 起会在插件日志(`$DSH_HOME/mobile-access/logs/dsh-mobile.log`)记录有限的 `auth failure` HTTP 诊断:区分授权与 CSRF 拒绝,包含状态、方法、安全路径或路径类别、相关 Cookie/请求头是否存在及计数、规范化 Origin 和受支持的 fetch-site 值,不记录令牌、Cookie 值、查询参数或任意原始路径。每个网关的两类诊断分别限频,首条立即记录,30 秒窗口内同类拒绝聚合;`suppressed` 表示此前被抑制的同类记录数量,不是全部请求数,也不限定于当前路径。它帮助排查凭据未携带或 CSRF 校验失败,不会自动修复 401/403。没有日志也不代表认证成功:更早的 Host/Origin 拒绝、WebSocket 升级和原生配对/续期不在这两处记录范围内。分享前仍应检查日志。
21
+ 插件 0.6.11 起会在插件日志(`$DSH_HOME/mobile-access/logs/dsh-mobile.log`)记录有限的 `auth failure` HTTP 诊断:区分授权与 CSRF 拒绝,包含状态、方法、安全路径或路径类别、相关 Cookie/请求头是否存在及计数、规范化 Origin、受支持的 fetch-site 值,以及有授权时设备标识的前 8 位,不记录令牌、Cookie 值、查询参数或任意原始路径。每个网关的两类诊断分别限频,首条立即记录,30 秒窗口内同类拒绝聚合;`suppressed` 表示此前被抑制的同类记录数量,不是全部请求数,也不限定于当前路径。它帮助排查凭据未携带或 CSRF 校验失败,不会自动修复 401/403。没有日志也不代表认证成功:更早的 Host/Origin 拒绝、WebSocket 升级和原生配对/续期不在这两处记录范围内。分享前仍应检查日志。
22
22
 
23
23
  ### 内部认证交换诊断(插件 0.6.15 起)
24
24
 
@@ -24,3 +24,20 @@ DSH Mobile's default Windows HTTPS / Discovery inbound rules allow only `LocalSu
24
24
  A routed network may not support LAN broadcast discovery. If the computer is not discovered, use its displayed pairing link or QR code; routing, firewall access, and the HTTPS entry must still work. Adding an IPv6 CIDR does not create an IPv6 listener.
25
25
 
26
26
  This setting controls source access to the LAN Gateway, not source CIDRs for an external reverse proxy. Configure that separately with the [own HTTPS reverse proxy guide](SELF_HOSTED_ORIGIN.en.md). Do not expose the LAN Gateway or private HTTP backend directly to the public internet.
27
+
28
+ ## Accessing the NAS administration page
29
+
30
+ The administration page's `/api/mobile-access` and the phone Gateway are separate entries. The additional trusted networks above do not open the management API. By default, the management API still accepts only direct loopback peers.
31
+
32
+ Plugin 0.6.18 adds `adminAllowedCidrs` for NAS deployments already accessed through a controlled LAN. Add this field to the existing `mobile-access` entry's complete `config`, replacing the example with the administration computer's actual address:
33
+
34
+ ```yaml
35
+ adminAllowedCidrs:
36
+ - 192.168.50.23/32
37
+ ```
38
+
39
+ This is only the additional field, **not a complete replacement `config`**. DSH entry overrides replace the whole configuration; retain the existing `setupFile`, `stateFile`, `controlFile`, TLS and other custom settings. Restart DSH after editing.
40
+
41
+ This option does not change DSH's listener, routing or firewall, or open a public management entry. It accepts only the listed direct TCP peers, never `X-Forwarded-For`, and also requires the current request to pass DSH session authentication. Sign in to DSH normally first. A version without usable authentication, a deployment with authentication disabled or a request without credentials cannot gain management access merely through a CIDR. Prefer a single administration computer's `/32`; the limit is 32 entries, with no all-address `/0` or duplicate networks.
42
+
43
+ Existing private-IP/loopback Host restrictions, exact Origin checks, cross-site rejection and denied CORS preflights remain. The management API does not accept arbitrary DNS names or public Hosts. A proxy's direct address may represent several visitors: the proxy must restrict access, retain DSH authentication and never add CORS permission for this path. Plain HTTP on a LAN does not prevent interception; use HTTPS or SSH forwarding on uncontrolled networks, and never publish `/api/mobile-access`. Remove the field or set an empty list to restore the default without deleting pairings.
@@ -24,3 +24,20 @@ DSH Mobile 的默认 Windows HTTPS / Discovery 入站规则只允许 `LocalSubne
24
24
  路由网络不一定支持局域网广播发现。发现列表没有电脑时,可使用电脑显示的配对链接或二维码,但路由、防火墙和 HTTPS 入口仍须可达。填写 IPv6 CIDR 也不会自动增加 IPv6 监听。
25
25
 
26
26
  此设置用于局域网 Gateway 的来源访问范围,不改变外部反向代理的来源 CIDR。自有代理请按[自有 HTTPS 反向代理指南](SELF_HOSTED_ORIGIN.md)配置;不要把局域网 Gateway 或私有 HTTP 后端映射到公网。
27
+
28
+ ## NAS 的管理网页访问
29
+
30
+ 管理网页的 `/api/mobile-access` 与手机连接的 Gateway 是两个入口。上面的额外可信网段设置不会开放管理 API。默认情况下,管理 API 仍只允许直连来源为回环地址。
31
+
32
+ 插件 0.6.18 起提供 `adminAllowedCidrs`,供已经通过受控局域网访问 DSH 的 NAS 部署使用。在现有 `mobile-access` 条目的完整 `config` 中追加以下字段,并将示例改成管理电脑的实际地址:
33
+
34
+ ```yaml
35
+ adminAllowedCidrs:
36
+ - 192.168.50.23/32
37
+ ```
38
+
39
+ 这只是新增字段,**不是可直接替换的完整 `config`**。DSH 的条目覆盖会替换整个配置;请保留原有 `setupFile`、`stateFile`、`controlFile`、TLS 和其他自定义设置。修改后重启 DSH。
40
+
41
+ 此选项不改变 DSH 的监听地址、路由或防火墙,也不会打开公网管理入口。仅允许列出的直接 TCP 来源,不读取 `X-Forwarded-For`;同时要求当前请求通过 DSH 的会话认证。浏览器须先正常登录 DSH;没有可用认证能力的版本、关闭认证的部署或无凭据请求不能靠填写 CIDR获得管理权限。尽量使用单台管理电脑的 `/32`,最多 32 项,不接受全地址 `/0` 或重复网段。
42
+
43
+ 现有私有 IP/回环 Host 限制、精确 Origin、跨站检查和拒绝 CORS 预检的行为保持不变。管理 API 不接受任意域名或公网 Host。反向代理的直接地址可能代表多名访问者:代理必须限制访问、保留 DSH 认证、不能给此路径添加跨域许可。HTTP 局域网也不能防止链路窃听;不受控网络请使用 HTTPS 或 SSH 转发,不要公开 `/api/mobile-access`。恢复默认时移除该字段或设为空列表,不会删除配对数据。