dsh-mobile 0.6.8 → 0.6.10
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.
- package/CHANGELOG.md +22 -3
- package/CONTRIBUTORS.md +6 -3
- package/README.en.md +15 -7
- package/README.md +15 -7
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/apps/mobile/README.md +11 -7
- package/apps/mobile/release.json +3 -3
- package/docs/CADDY_MANAGED.en.md +21 -17
- package/docs/CADDY_MANAGED.md +21 -17
- package/docs/README.md +3 -3
- package/docs/SELF_HOSTED_FRP.en.md +1 -1
- package/docs/SELF_HOSTED_FRP.md +1 -1
- package/docs/SELF_HOSTED_ORIGIN.en.md +3 -1
- package/docs/SELF_HOSTED_ORIGIN.md +4 -2
- package/lib/client.js +63 -50
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +214 -46
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/packages/question-fixes/package.json +1 -1
package/docs/CADDY_MANAGED.md
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
[English](CADDY_MANAGED.en.md)
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> 插件 0.6.9 起,Windows/Linux x64 可按需安装托管 Caddy,由插件管理 HTTPS 与证书。组件来自 DSH Mobile 审核发布的固定构建,不是 Caddy 官方定制下载接口。已有 Cloudflared 等通道和自备反向代理继续可用,无需迁移或升级 Android App。
|
|
6
6
|
|
|
7
7
|
## 适用条件
|
|
8
8
|
|
|
9
9
|
托管 Caddy 为自建连接管理 HTTPS 代理和证书,不提供内网穿透。你需要自己的域名、由腾讯云 DNSPod 管理的 DNS,以及可从公网访问的入站端口;路由器需把公网端口映射到运行 DSH 的电脑。CGNAT、校园网隔离或没有入站权限的网络不能仅靠 DNS-01 绕过限制,可以使用 Cloudflare Tunnel、cpolar,或已有 VPS 上的 FRP。
|
|
10
10
|
|
|
11
|
+
当前面板仅提供腾讯云 DNSPod 的 DNS-01 配置。使用 Cloudflare DNS 的用户可以继续使用 Cloudflared,无需为本功能迁移 DNS;不要把 Cloudflared 连接器 Token 填入腾讯云密钥栏。
|
|
12
|
+
|
|
11
13
|
```text
|
|
12
14
|
Android App / 浏览器 → 公网 HTTPS 域名(Caddy 终止 TLS)
|
|
13
15
|
→ 127.0.0.1 上的独立 HTTP Gateway
|
|
@@ -18,11 +20,15 @@ Android App / 浏览器 → 公网 HTTPS 域名(Caddy 终止 TLS)
|
|
|
18
20
|
|
|
19
21
|
两者属于「自备反向代理」提供方的不同上游模式。默认仍为自备代理:你维护 Lucky、Nginx 或 Caddy,插件只提供 HTTP 后端。切换为托管模式后,插件拥有一个独立的 Caddy 子进程和回环 Gateway,先停止旧模式再启动新模式;原有代理配置和已配对设备记录保留,不会被托管配置覆盖。
|
|
20
22
|
|
|
21
|
-
公网入口、DNS-01 凭据与本机 HTTPS 端口需单独配置。公网 URL 可以使用 443,而路由器转发到本机 8443;「本机 HTTPS 端口」是 Caddy
|
|
23
|
+
公网入口、DNS-01 凭据与本机 HTTPS 端口需单独配置。公网 URL 可以使用 443,而路由器转发到本机 8443;「本机 HTTPS 端口」是 Caddy 实际绑定的端口,范围为 1024–65535,3080、3443、3444 保留不可选。确认本机端口未占用,并在主机防火墙、路由器或云安全组中放行所需 HTTPS 入站;不要开放回环 HTTP 后端。DNS-01 凭据应使用仅能管理目标 DNS 区域的最小权限账号,不要提供云账号的主密钥。
|
|
22
24
|
|
|
23
25
|
## 安装、启动与就绪
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
1. 在电脑端打开 **移动访问 → 远程 → 自建连接 → 自有反向代理**,选择 **托管 Caddy**。
|
|
28
|
+
2. 点击 **安装托管 Caddy** 并确认下载。插件从固定的 [DSH Mobile 组件分发](https://github.com/saya-ch/dsh-mobile/releases/tag/caddy-component-2.11.7-tencentcloud-0.4.3-r1) 下载,核对完整大小、SHA-256、Caddy 核心与 DNS 模块版本;来源不可用或校验失败时不会执行文件。
|
|
29
|
+
3. 填写公网 HTTPS 地址,例如 `https://dsh.example.com`,选择腾讯云 DNSPod,填写具有所需区域权限的 `SecretId` 和 `SecretKey`,再设置本机 HTTPS 端口,例如 `8443`。让域名解析到你的公网入口,并按需将公网 TCP 443 映射至电脑的 8443。
|
|
30
|
+
4. 点击 **保存并启动 Caddy**。组件已安装不等于连接就绪:只有公网 HTTPS 证书通过验证,并确认入口属于当前 DSH 实例后,面板才显示已就绪。
|
|
31
|
+
5. 生成远程配对二维码,在 Android App 的 **远程访问** 中扫码,或用手机浏览器打开配对链接。用手机流量验证实际页面加载、会话同步与 WebSocket;不要只以电脑端健康检查代替手机访问。
|
|
26
32
|
|
|
27
33
|
首次 DNS-01 签发需要等待 DNS 生效和证书签发。期间电脑保持在线,避免反复重启触发签发限流。托管模式不安装系统服务、不修改 PATH、不设置开机启动、不导入系统证书;Caddy 的管理 API 和自动配置落盘均关闭。
|
|
28
34
|
|
|
@@ -30,13 +36,13 @@ Android App / 浏览器 → 公网 HTTPS 域名(Caddy 终止 TLS)
|
|
|
30
36
|
|
|
31
37
|
设置与 DNS 凭据原子保存在插件私有目录,Caddyfile 只包含环境变量占位符。子进程只获得必要的 DNS 凭据和运行环境,不继承其他 API 密钥或代理、管理口配置;原始 DNS 模块日志不保存。证书、私钥、配置缓存和运行文件也保存在该私有目录。
|
|
32
38
|
|
|
33
|
-
停止访问或切换提供方会等待 Caddy 与 Gateway
|
|
39
|
+
停止访问或切换提供方会等待 Caddy 与 Gateway 退出,但保留设置和证书,以便下次连接。确认「移除托管 Caddy 与配置」后删除受管二进制、DNS 凭据、证书私钥、日志与缓存,不删除其他提供方、局域网或设备配对数据。清理失败会报告错误并保留可重试的状态,不会把未完成的删除显示为成功。已有手动安装的代理不归插件所有,也不会被插件卸载。
|
|
34
40
|
|
|
35
41
|
## 维护者构建与验证
|
|
36
42
|
|
|
37
43
|
以下构建和验证工具需在本仓库源码 checkout 中运行;npm 插件包不包含这些维护脚本,也不需要普通用户自行编译 Caddy。
|
|
38
44
|
|
|
39
|
-
[构建脚本](<../scripts/build-caddy-component.mjs>) 固定 Go 1.26.
|
|
45
|
+
[构建脚本](<../scripts/build-caddy-component.mjs>) 固定 Go 1.26.9、xcaddy v0.4.7、Caddy v2.11.7、腾讯云 DNS v0.4.3 及 golang.org/x/net v0.60.0。[输入锁](<../scripts/caddy-component-lock.json>) 固定源码提交和编译依赖版本/校验和。每次构建使用全新的私有编译缓存及模块缓存,执行 Go 校验和、源码来源与保留模块校验;未知依赖、替换模块、编译器或原生设置漂移会在运行生成的 Caddy 前拒绝。
|
|
40
46
|
|
|
41
47
|
选择精确 Go 编译器(可用 `GO_BINARY`),输出目录必须是全新绝对路径:
|
|
42
48
|
|
|
@@ -48,28 +54,26 @@ node scripts/compare-caddy-builds.mjs --first /absolute/new/first --second /abso
|
|
|
48
54
|
|
|
49
55
|
Windows 使用原生绝对路径和 `--target win32-x64`。可复现指二进制字节、大小、SHA-256 一致,不保证操作系统镜像或所有元数据/许可证文本逐字一致。[工作流](<../.github/workflows/caddy-component.yml>) 在 Windows/Linux x64 分别独立构建两次,审核完整产物,并使用独立 manifest 测试真正安装后的二进制:默认版本/模块检查、目录提升、无重复下载的重启、损坏下载回滚、TLS/配对/API/WSS、持久设备凭据与停止后清理,保留邻近文件及配对数据。其他架构仍未审核。
|
|
50
56
|
|
|
51
|
-
PR 和普通手动运行只产生只读审查产物。只有授权的 `abworks-dev/dsh-mobile` fork 上已有 `caddy-component-2.11.
|
|
57
|
+
PR 和普通手动运行只产生只读审查产物。只有授权的 `abworks-dev/dsh-mobile` fork 上已有 `caddy-component-2.11.7-tencentcloud-0.4.3-review.N` 标签,经明确手动启用且两个原生任务均通过,才发布 **TEST 预发布版**:
|
|
52
58
|
|
|
53
59
|
```sh
|
|
54
|
-
gh workflow run caddy-component.yml --repo abworks-dev/dsh-mobile --ref caddy-component-2.11.
|
|
60
|
+
gh workflow run caddy-component.yml --repo abworks-dev/dsh-mobile --ref caddy-component-2.11.7-tencentcloud-0.4.3-review.1 -f publish_review_prerelease=true
|
|
55
61
|
```
|
|
56
62
|
|
|
57
63
|
组件标签不以 `v` 开头,避免触发插件正式发布。独立写权限任务拒绝已有 Release,创建非 latest 的预发布草稿,不覆盖资产;下载所有草稿资产与准备字节逐一对比后才公开。产物包括区分平台的原始二进制、manifest、编译/模块/版本/许可证证据、双构建证明、源码提交/运行标识和 SHA256SUMS。[发布后验证器](<../scripts/verify-caddy-release.mjs>) 用 GitHub 元数据及安装器实际 HTTPS/受限重定向下载链路复核 **所有公开资产**,成功后才输出仅供审核的目录。公开后验证失败会保留已公开的标记预发布版、使任务失败且不输出校验目录,不擅自删除或回滚 Release。CI 产物保留 14 天,Release 资产另行保存。
|
|
58
64
|
|
|
59
|
-
公开定制下载端点曾在请求 v2.11.6 时返回 v2.11.7,动态最新版不能成为信任来源。版本化 GitHub URL 仍可被仓库所有者替换;信任边界是审核过的精确大小/哈希,而非 URL 名称或远端自报校验和。fork 的目录只是审核候选,不会自动写入[生产组件表](<../src/caddy-component.ts>)
|
|
60
|
-
|
|
61
|
-
### 官方分发需要独立的两阶段授权
|
|
65
|
+
公开定制下载端点曾在请求 v2.11.6 时返回 v2.11.7,动态最新版不能成为信任来源。版本化 GitHub URL 仍可被仓库所有者替换;信任边界是审核过的精确大小/哈希,而非 URL 名称或远端自报校验和。fork 的目录只是审核候选,不会自动写入[生产组件表](<../src/caddy-component.ts>)。
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
### 正式分发与安装开放
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
正式组件由有仓库发布权限的维护者确认候选后手动发布,单维护者即可完成;不要求第二个人批准,也不要求预先配置 GitHub Environment。两个发布输入默认 false,PR 和普通构建不会发布。贡献者的实测可以按其版本、环境和实际验证范围作为验收依据,不要求在维护者本机重复搭建同一环境;不得将隔离测试或旧候选结果写成新二进制的公网签发、续期验收。
|
|
66
70
|
|
|
67
|
-
|
|
71
|
+
官方发布仅允许公开的 `saya-ch/dsh-mobile` 仓库和版本绑定的非 `v` 标签 `caddy-component-2.11.7-tencentcloud-0.4.3-r1`。维护者在已审核的 main 提交上创建该标签,然后手动运行现有工作流,显式设置 `publish_official_release=true`,保持 review 输入 false。已创建的标签不移动或覆盖;本次发布准备修正使用新的固定 `-r1` 修订标签,原标签保留。发布器检查标签 SHA 与本次 Actions SHA 相同、提交属于 main,并要求同一次运行的 Windows/Linux 原生双构建、测试和插件构建通过。错误事件、分支、仓库、互斥输入、失败原生任务或不匹配标签均拒绝发布。
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
唯一发布任务使用 `contents: write` 和现有 `GITHUB_TOKEN`,创建非 latest 的正式组件分发,不发布 npm/APK。已占用的 Release 不覆盖;全部 16 个草稿资产下载后逐字节比较,再公开并通过安装器实际 HTTPS 下载链路独立复核公开元数据、大小和哈希。验证失败保留实际已发布状态并使任务失败,不擅自删除或破坏性重试。
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
组件更新分两阶段:先发布并验证官方分发,再通过单独审核的插件目录变更,把实际公开二进制的来源、大小和 SHA-256 固定到 `CADDY_COMPONENT_RELEASES`。0.6.9 开放 Windows/Linux x64 的固定目录;将来发布新的组件资产不会自动更换用户运行的版本。
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
prepare/verify CLI 默认 review;尾参数 `--channel official` 选择官方身份校验。官方验证成功后生成 `official-candidate-not-production`,仍保持 `productionCatalogEnabled: false`:该 JSON 是审核输入,不是运行时配置,也不会自行启用安装。贡献者的实网反馈按其版本与部署范围采用。
|
|
74
78
|
|
|
75
|
-
|
|
79
|
+
隔离验证使用回环随机端口、私有内部 CA 与 `skip_install_trust`,不修改系统信任、生产 DNS 或现有代理。本次固定组件尚未取得另行的公网 DNS-01/ACME 签发与长周期续期验收;证书签发、续期、公网可达和实际页面/API/流传递应分别验证,不能由隔离 CI 或旧候选的实测替代。
|
package/docs/README.md
CHANGED
|
@@ -12,20 +12,20 @@
|
|
|
12
12
|
| 在已有 VPS 上新部署 FRP 与 HTTPS 入口 | [自建 FRP](SELF_HOSTED_FRP.md) | [Self-hosted FRP](SELF_HOSTED_FRP.en.md) |
|
|
13
13
|
| 复用已经运行的 frps,不自动修改服务器 | [接入既有 frps](ATTACH_EXISTING_FRPS.md) | [Existing frps](ATTACH_EXISTING_FRPS.en.md) |
|
|
14
14
|
| 复用 Lucky、Nginx 或 Caddy 的公开 HTTPS 入口 | [自有反向代理](SELF_HOSTED_ORIGIN.md) | [Own reverse proxy](SELF_HOSTED_ORIGIN.en.md) |
|
|
15
|
+
| 由插件安装 Caddy 并管理 HTTPS 证书(0.6.9 起) | [托管 Caddy](CADDY_MANAGED.md) | [Managed Caddy](CADDY_MANAGED.en.md) |
|
|
15
16
|
| 页面加载慢、实时连接反复重连或长操作超时 | [慢链路与重连](SLOW_CONNECTIONS.md) | [Slow links and reconnection](SLOW_CONNECTIONS.en.md) |
|
|
16
17
|
| 保留电脑皮肤,排查移动页面的背景与可读性 | [皮肤与移动布局](SKIN_COMPATIBILITY.md) | [Skins and mobile layout](SKIN_COMPATIBILITY.en.md) |
|
|
17
18
|
|
|
18
19
|
在 Android App 中连接公开隧道或自建入口时请选择 **远程访问**。公开受信任证书也可用手机浏览器配对;既有 frps 的自签入口需要 **0.4.6 或更高版本 App** 固定远程 CA。各通道仍要求 DSH Mobile 配对,不能直接暴露普通 DSH 或私有 HTTP 后端。
|
|
19
20
|
|
|
20
|
-
##
|
|
21
|
+
## 页面与网络设置 / Page and network settings
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
按需选择移动页面模块和受信任的局域网来源;这些设置不会创建 VPN,也不会自动刷新当前会话。
|
|
23
24
|
|
|
24
25
|
| 功能 | 中文 | English | 使用状态 |
|
|
25
26
|
| --- | --- | --- | --- |
|
|
26
27
|
| 为移动页面选择加载模块 | [模块设置](CLIENT_MODULES.md) | [Module settings](CLIENT_MODULES.en.md) | 电脑默认与设备覆盖分别保存,不自动刷新会话 |
|
|
27
28
|
| 允许受控路由网络访问 LAN Gateway | [额外可信网段](TRUSTED_NETWORKS.md) | [Trusted networks](TRUSTED_NETWORKS.en.md) | 不建立 VPN,不自动扩大防火墙规则 |
|
|
28
|
-
| 由插件管理 Caddy HTTPS 上游 | [托管 Caddy](CADDY_MANAGED.md) | [Managed Caddy](CADDY_MANAGED.en.md) | 尚无可信固定二进制分发,安装与连接仍禁用 |
|
|
29
29
|
|
|
30
30
|
## 开发与安全 / Development and security
|
|
31
31
|
|
|
@@ -29,7 +29,7 @@ The publicly trusted entry in this guide requires Android app 0.3.3 or later for
|
|
|
29
29
|
|
|
30
30
|
Before deployment, the panel lists every VPS change. It installs Caddy from its official APT source; public IPv4 mode additionally installs a Python virtual environment, Certbot and the renewal timer. It creates the frps service/configuration, Caddy snippet and one import line, and allows the FRP control port plus 80/tcp and 443/tcp when UFW is active. An existing `dsh-mobile` system user is reused and retained during cleanup; a user created by this deployment is removed during cleanup. An existing DSH Mobile import is normalized to one line. Merge custom sites manually; the version-specific behavior is described below.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Since plugin 0.6.4, automatic deployment recognizes a replaceable default Caddyfile by its complete file hash. A default site with additional sites, or a file carrying only the legacy management marker, requires manual merging. Retain a backup when custom sites exist and do not replace the entire file with the generated template. Remove legacy whole-file DSH Mobile sites manually during cleanup while preserving other content.
|
|
33
33
|
|
|
34
34
|
## Verify the host keys every time
|
|
35
35
|
|
package/docs/SELF_HOSTED_FRP.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
点部署按钮前,面板会列出自动部署将在 VPS 上做的全部改动:从官方 APT 源安装 Caddy;仅在公网 IPv4 模式安装 Python venv、Certbot 和证书续期定时器;创建 `dsh-mobile` 系统用户、frps 配置与服务、Caddy 片段和主文件里的一行 import;如果 UFW 已启用,则放行 FRP 控制端口与 80/443。已存在的系统用户会复用并在清理时保留,本次部署创建的用户则会在清理时删除。主 Caddyfile 已有 DSH Mobile import 时,只整理该行;已有自定义站点时请手动合并,版本差异见下方说明。
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
从插件 0.6.4 起,自动部署使用完整默认文件哈希识别可替换的 Caddyfile;保留默认站点后追加其他站点的文件,以及只有旧版管理标记的文件,都需要手动合并。已有自定义站点时保留备份,不要用生成模板替换整个文件。清理旧版整文件配置时也应自行移除 DSH Mobile 站点,保留其他内容。
|
|
37
37
|
|
|
38
38
|
## 主机指纹核对(每次必做)
|
|
39
39
|
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
> This provider ships from **0.4.2** onward.
|
|
6
6
|
|
|
7
|
+
This guide covers the default **external proxy** mode, where you manage public HTTPS. To have the plugin install and manage Caddy and certificates instead, choose [Managed Caddy](CADDY_MANAGED.en.md) in the same panel. Its configuration and readiness checks are separate.
|
|
8
|
+
|
|
7
9
|
## When to use it
|
|
8
10
|
|
|
9
11
|
Use an existing Lucky, Nginx or Caddy HTTPS proxy rather than a tunnel. Under **Mobile Access → Remote → Self-hosted connection → Own reverse proxy**, DSH Mobile creates a separate private HTTP backend with device authentication:
|
|
@@ -51,7 +53,7 @@ For a separate proxy host, configure routing and a firewall rule limited to its
|
|
|
51
53
|
|
|
52
54
|
## State and phone verification
|
|
53
55
|
|
|
54
|
-
**Backend listening proves the local listener only, not public HTTPS, certificates or WebSocket connectivity.**
|
|
56
|
+
**Backend listening proves the local listener only, not public HTTPS, certificates or WebSocket connectivity.** External-proxy diagnostics do not probe the public origin.
|
|
55
57
|
|
|
56
58
|
Generate a remote pairing QR code and use **Remote access** in Android app **0.4.0 or later**, not Local network. A publicly trusted origin also supports mobile-browser pairing. Verify from the intended external network:
|
|
57
59
|
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
> 本功能自 **0.4.2** 起随包发布。
|
|
6
6
|
|
|
7
|
+
本页介绍默认的**自备代理**模式,由你维护公网 HTTPS 代理。希望插件按需安装并管理 Caddy 与证书时,请使用同一面板中的[托管 Caddy](CADDY_MANAGED.md)模式;两者的配置与就绪检查不同。
|
|
8
|
+
|
|
7
9
|
## 适用场景与安全边界
|
|
8
10
|
|
|
9
11
|
你已有 Lucky、Nginx、Caddy 等 HTTPS 反向代理,不需要 Funnel、cpolar 或 FRP 隧道。进入电脑端 **移动访问 → 远程 → 自建连接 → 自有反向代理**,让插件提供一个独立的、带设备配对认证的私有 HTTP 后端:
|
|
@@ -17,7 +19,7 @@
|
|
|
17
19
|
- **只有第一段允许暴露公网。HTTP 后端不能做公网端口映射。** HTTP 段仅适用于可信私网;局域网其他设备上的代理也会接触明文流量。
|
|
18
20
|
- 后端不是裸 DSH:仍执行设备配对、认证、归一化后精确匹配的 Host/Origin 校验、CSRF、Secure Cookie 和 WebSocket 路径策略。没有配对的访问不能直接进入 DSH。**不要用浏览器直接打开这个明文 HTTP 后端做验证**:它只支持由指定 HTTPS 公网地址代理的请求;直接打开时 Host 不匹配,Cookie 与 CSRF 流程也可能失效。
|
|
19
21
|
- 不要把公网反代目标改成 DSH 的 WebServer 端口(默认 3080),也不要反代现有 LAN HTTPS 3443。新后端默认 3444,**明确拒绝 3443 和 3080**,并拒绝 1024 以下需要特权的端口。注意 3444 同时是 cloudflared 命名隧道的默认转发端口:同一时刻只有一个远程提供方能运行,但切换到本提供方时请确认该端口已释放。DSH 与 LAN 的原有设置不变。
|
|
20
|
-
- 公网 TLS、证书续期、DNS、路由和代理由你管理。插件不会安装隧道组件、部署 VPS
|
|
22
|
+
- 公网 TLS、证书续期、DNS、路由和代理由你管理。插件不会安装隧道组件、部署 VPS、修改代理/防火墙/路由器,也不会在自备代理模式中主动探测公网入口。
|
|
21
23
|
|
|
22
24
|
## 填写四项设置
|
|
23
25
|
|
|
@@ -52,7 +54,7 @@ CIDR 限制针对 TCP 连接的**直接来源地址**,不是手机 IP,也不
|
|
|
52
54
|
|
|
53
55
|
## 状态与手机验证
|
|
54
56
|
|
|
55
|
-
**“后端已监听”只证明本地 HTTP 监听成功,不代表公网 HTTPS、证书或 WebSocket 已成功。**
|
|
57
|
+
**“后端已监听”只证明本地 HTTP 监听成功,不代表公网 HTTPS、证书或 WebSocket 已成功。** 自备代理模式的连接诊断也只报告本地状态,不自动向公网地址探测。
|
|
56
58
|
|
|
57
59
|
在电脑端点击 **生成远程配对二维码**,再在 Android App **0.4.0 或更高版本**的 **远程访问** 流程扫码(不要使用 LAN 扫码流程)。现场验证中 0.3.16 会拒绝自有域名配对;现有自有域名逻辑保留 HTTPS 自定义端口,无需为此功能修改 APK。
|
|
58
60
|
|
package/lib/client.js
CHANGED
|
@@ -587,7 +587,7 @@ window.__ModuleLoader__.load({
|
|
|
587
587
|
trustedNetworksFailed: "Could not read this setting. Retry after reopening this section.",
|
|
588
588
|
trustedNetworksInvalid: "Could not save. Enter valid network CIDRs, for example 11.24.0.0/24, and check that DSH can write its private setup file.",
|
|
589
589
|
cpolarDownloadFailed: "Could not download cpolar from its official site. Check this computer’s network or proxy, then retry.",
|
|
590
|
-
caddyUnavailable: "
|
|
590
|
+
caddyUnavailable: "Managed Caddy is not available for this system or architecture. Use an existing reverse proxy or another remote provider; unverified downloads will not run.",
|
|
591
591
|
caddyVerified: "Managed HTTPS entry verified for this computer.",
|
|
592
592
|
caddyInstalled: "Caddy component installed; the connection is not yet verified.",
|
|
593
593
|
caddyConnectionFailed: "Managed HTTPS connection was not established. Check the domain, public inbound access, port mapping and DNS credentials.",
|
|
@@ -617,7 +617,7 @@ window.__ModuleLoader__.load({
|
|
|
617
617
|
trustedNetworksFailed: "Impossibile leggere questa impostazione. Riapri la sezione e riprova.",
|
|
618
618
|
trustedNetworksInvalid: "Salvataggio non riuscito. Inserisci CIDR validi, ad esempio 11.24.0.0/24, e verifica che DSH possa scrivere il file privato di configurazione.",
|
|
619
619
|
cpolarDownloadFailed: "Impossibile scaricare cpolar dal sito ufficiale. Controlla rete o proxy del computer e riprova.",
|
|
620
|
-
caddyUnavailable: "
|
|
620
|
+
caddyUnavailable: "Caddy gestito non è disponibile per questo sistema o architettura. Usa il tuo proxy o un altro provider; i download non verificati non vengono eseguiti.",
|
|
621
621
|
caddyVerified: "Ingresso HTTPS gestito verificato per questo computer.",
|
|
622
622
|
caddyInstalled: "Componente Caddy installato; connessione non ancora verificata.",
|
|
623
623
|
caddyConnectionFailed: "Connessione HTTPS gestita non stabilita. Controlla dominio, accesso pubblico, porte e credenziali DNS.",
|
|
@@ -638,7 +638,7 @@ window.__ModuleLoader__.load({
|
|
|
638
638
|
});
|
|
639
639
|
Object.assign(MOBILE_CONTROL_MESSAGES.zh, {
|
|
640
640
|
cpolarDownloadFailed: "无法从 cpolar 官网下载组件。请检查这台电脑的网络或代理,然后重试。",
|
|
641
|
-
caddyUnavailable: "
|
|
641
|
+
caddyUnavailable: "当前系统或架构暂不支持托管 Caddy。请使用已有反向代理或其他远程方式;不会执行未经校验的下载。",
|
|
642
642
|
caddyVerified: "托管 HTTPS 入口已验证为这台电脑。",
|
|
643
643
|
caddyInstalled: "Caddy 组件已安装,远程连接尚未验证。",
|
|
644
644
|
caddyConnectionFailed: "托管 HTTPS 连接未建立,请检查域名、公网入站、端口映射和 DNS 凭据。",
|
|
@@ -1236,12 +1236,12 @@ window.__ModuleLoader__.load({
|
|
|
1236
1236
|
caddyModeExternal: "I already run my own reverse proxy",
|
|
1237
1237
|
caddySectionTitle: "Managed Caddy upstream",
|
|
1238
1238
|
caddyInstall: "Install managed Caddy",
|
|
1239
|
-
caddyInstallNote: "
|
|
1239
|
+
caddyInstallNote: "Downloads the pinned Caddy core + Tencent Cloud DNS build published by DSH Mobile, verifies its size and SHA-256, and stores it in the plugin private directory. No system service, PATH entry, or startup item is added.",
|
|
1240
1240
|
caddyDomain: "Public domain",
|
|
1241
1241
|
caddyDnsProvider: "DNS provider (DNS-01)",
|
|
1242
1242
|
caddySecretId: "Tencent Cloud SecretId",
|
|
1243
1243
|
caddySecretKey: "Tencent Cloud SecretKey",
|
|
1244
|
-
caddyListenPort: "
|
|
1244
|
+
caddyListenPort: "Local HTTPS port",
|
|
1245
1245
|
caddySaveConnect: "Save and start Caddy",
|
|
1246
1246
|
caddyPurge: "Remove managed Caddy and settings",
|
|
1247
1247
|
caddyReady: "Managed Caddy is running; certificates are issued and renewed automatically.",
|
|
@@ -1295,12 +1295,12 @@ window.__ModuleLoader__.load({
|
|
|
1295
1295
|
caddyModeExternal: "Uso già il mio reverse proxy",
|
|
1296
1296
|
caddySectionTitle: "Upstream Caddy gestito",
|
|
1297
1297
|
caddyInstall: "Installa Caddy gestito",
|
|
1298
|
-
caddyInstallNote: "
|
|
1298
|
+
caddyInstallNote: "Scarica la build Caddy + DNS Tencent Cloud a versione fissa pubblicata da DSH Mobile, verifica dimensione e SHA-256 e la conserva nella directory privata del plugin. Non aggiunge servizi di sistema, voci PATH o avvio automatico.",
|
|
1299
1299
|
caddyDomain: "Dominio pubblico",
|
|
1300
1300
|
caddyDnsProvider: "Provider DNS (DNS-01)",
|
|
1301
1301
|
caddySecretId: "SecretId Tencent Cloud",
|
|
1302
1302
|
caddySecretKey: "SecretKey Tencent Cloud",
|
|
1303
|
-
caddyListenPort: "Porta HTTPS
|
|
1303
|
+
caddyListenPort: "Porta HTTPS locale",
|
|
1304
1304
|
caddySaveConnect: "Salva e avvia Caddy",
|
|
1305
1305
|
caddyPurge: "Rimuovi Caddy gestito e impostazioni",
|
|
1306
1306
|
caddyReady: "Caddy gestito in esecuzione; i certificati vengono emessi e rinnovati automaticamente.",
|
|
@@ -1354,12 +1354,12 @@ window.__ModuleLoader__.load({
|
|
|
1354
1354
|
caddyModeExternal: "我已自行配置反向代理",
|
|
1355
1355
|
caddySectionTitle: "托管 Caddy 上游",
|
|
1356
1356
|
caddyInstall: "安装托管 Caddy",
|
|
1357
|
-
caddyInstallNote: "
|
|
1357
|
+
caddyInstallNote: "下载 DSH Mobile 发布的固定 Caddy 核心 + 腾讯云 DNS 构建,校验精确大小和 SHA-256 后保存在插件私有目录。不添加系统服务、PATH 条目或开机启动项。",
|
|
1358
1358
|
caddyDomain: "公网域名",
|
|
1359
1359
|
caddyDnsProvider: "DNS 提供商(DNS-01)",
|
|
1360
1360
|
caddySecretId: "腾讯云 SecretId",
|
|
1361
1361
|
caddySecretKey: "腾讯云 SecretKey",
|
|
1362
|
-
caddyListenPort: "
|
|
1362
|
+
caddyListenPort: "本机 HTTPS 端口",
|
|
1363
1363
|
caddySaveConnect: "保存并启动 Caddy",
|
|
1364
1364
|
caddyPurge: "移除托管 Caddy 与配置",
|
|
1365
1365
|
caddyReady: "托管 Caddy 正在运行;证书会自动签发并续期。",
|
|
@@ -7564,9 +7564,60 @@ window.__ModuleLoader__.load({
|
|
|
7564
7564
|
/** Resolve the next-step text for a remote-controller diagnostic. */
|
|
7565
7565
|
function diagnosticControllerAction(code, locale, fallback) {
|
|
7566
7566
|
if (typeof code !== "string") return fallback;
|
|
7567
|
-
const key = DIAGNOSTIC_CONTROLLER_ACTION_OVERRIDES[code] ?? (DIAGNOSTIC_CONTROLLER_ACTION_CODES.has(code) ? REMOTE_ERROR_MESSAGE_KEYS[code] : void 0);
|
|
7567
|
+
const key = (Object.prototype.hasOwnProperty.call(DIAGNOSTIC_CONTROLLER_ACTION_OVERRIDES, code) ? DIAGNOSTIC_CONTROLLER_ACTION_OVERRIDES[code] : void 0) ?? (DIAGNOSTIC_CONTROLLER_ACTION_CODES.has(code) ? REMOTE_ERROR_MESSAGE_KEYS[code] : void 0);
|
|
7568
7568
|
return key === void 0 ? fallback : controlTranslator(locale)(key);
|
|
7569
7569
|
}
|
|
7570
|
+
/**
|
|
7571
|
+
* Localize a diagnostic entry, retaining Host text for unknown diagnostic reasons.
|
|
7572
|
+
* @param entry Diagnostic fields received from the control API.
|
|
7573
|
+
* @param locale Language of the mounted control panel.
|
|
7574
|
+
* @param versions Optional version fields from the diagnostic response.
|
|
7575
|
+
* @returns Detail and next-step text shared by the visible card and copied report.
|
|
7576
|
+
*/
|
|
7577
|
+
function localizedDiagnosticEntryCopy(entry, locale, versions) {
|
|
7578
|
+
const t = controlTranslator(locale);
|
|
7579
|
+
const localeTag = locale === "it" ? "it-IT" : locale === "zh" ? "zh-CN" : "en-US";
|
|
7580
|
+
const serverCopy = diagnosticServerCopy(entry);
|
|
7581
|
+
const reason = typeof entry.reason === "string" ? entry.reason : "";
|
|
7582
|
+
const facts = entry.facts !== null && typeof entry.facts === "object" ? entry.facts : {};
|
|
7583
|
+
const copyReason = facts.viaProxy === true && (reason === "remote-ready" || reason === "remote-rate-limited") ? `${reason}-via-proxy` : reason;
|
|
7584
|
+
const catalog = DIAGNOSTIC_REASON_MESSAGES[locale];
|
|
7585
|
+
const templates = Object.prototype.hasOwnProperty.call(catalog, copyReason) ? catalog[copyReason] : void 0;
|
|
7586
|
+
if (templates === void 0) return serverCopy;
|
|
7587
|
+
const providerNames = {
|
|
7588
|
+
tailscale: "Tailscale Funnel",
|
|
7589
|
+
cpolar: "cpolar",
|
|
7590
|
+
cloudflared: "cloudflared",
|
|
7591
|
+
frp: t("frpName"),
|
|
7592
|
+
origin: t("originName")
|
|
7593
|
+
};
|
|
7594
|
+
const providerId = typeof facts.provider === "string" ? facts.provider : "";
|
|
7595
|
+
const controllerCode = typeof facts.controllerCode === "string" ? facts.controllerCode : "";
|
|
7596
|
+
const controllerKey = Object.prototype.hasOwnProperty.call(REMOTE_ERROR_MESSAGE_KEYS, controllerCode) ? REMOTE_ERROR_MESSAGE_KEYS[controllerCode] : void 0;
|
|
7597
|
+
const values = {
|
|
7598
|
+
provider: providerNames[providerId] ?? "",
|
|
7599
|
+
latencyMs: typeof facts.latencyMs === "number" && Number.isFinite(facts.latencyMs) ? new Intl.NumberFormat(localeTag).format(facts.latencyMs) : "",
|
|
7600
|
+
interfaceName: typeof facts.interfaceName === "string" ? facts.interfaceName : "",
|
|
7601
|
+
endpointSuffix: typeof facts.endpointSuffix === "string" ? facts.endpointSuffix : "",
|
|
7602
|
+
controllerCode
|
|
7603
|
+
};
|
|
7604
|
+
const interpolate = (template) => template.replace(/\{(\w+)\}/gu, (_match, key) => values[key] ?? "");
|
|
7605
|
+
let detail = reason === "remote-controller-error" && controllerCode === "" ? t("remoteError") : interpolate(templates[0]);
|
|
7606
|
+
let action = interpolate(templates[1]);
|
|
7607
|
+
if (reason === "versions-current") {
|
|
7608
|
+
const fields = versions !== null && typeof versions === "object" ? versions : {};
|
|
7609
|
+
if ([
|
|
7610
|
+
fields.plugin,
|
|
7611
|
+
fields.dsh,
|
|
7612
|
+
fields.minimumAndroidApp
|
|
7613
|
+
].every((value) => typeof value === "string")) detail += ` plugin ${String(fields.plugin)}, DSH ${String(fields.dsh)}, Android ${String(fields.minimumAndroidApp)}.`;
|
|
7614
|
+
}
|
|
7615
|
+
if (reason === "remote-controller-error") action = diagnosticControllerAction(controllerCode, locale, controllerKey === void 0 ? action : t(controllerKey));
|
|
7616
|
+
return {
|
|
7617
|
+
detail,
|
|
7618
|
+
action
|
|
7619
|
+
};
|
|
7620
|
+
}
|
|
7570
7621
|
function installControl() {
|
|
7571
7622
|
/** The table above, translated, for the status repaint. */
|
|
7572
7623
|
const translatedRemoteErrors = () => Object.fromEntries(Object.entries(REMOTE_ERROR_MESSAGE_KEYS).map(([code, key]) => [code, t(key)]));
|
|
@@ -10697,46 +10748,7 @@ window.__ModuleLoader__.load({
|
|
|
10697
10748
|
"phone-network": "diagnosticLabelPhone"
|
|
10698
10749
|
};
|
|
10699
10750
|
const statusOf = (entry) => normalizeDiagnosticStatus(entry.status);
|
|
10700
|
-
const localizedEntryCopy = (entry) =>
|
|
10701
|
-
const serverCopy = diagnosticServerCopy(entry);
|
|
10702
|
-
const reason = typeof entry.reason === "string" ? entry.reason : "";
|
|
10703
|
-
const facts = entry.facts !== null && typeof entry.facts === "object" ? entry.facts : {};
|
|
10704
|
-
const copyReason = facts.viaProxy === true && (reason === "remote-ready" || reason === "remote-rate-limited") ? `${reason}-via-proxy` : reason;
|
|
10705
|
-
const templates = DIAGNOSTIC_REASON_MESSAGES[locale][copyReason];
|
|
10706
|
-
if (templates === void 0) return serverCopy;
|
|
10707
|
-
const providerNames = {
|
|
10708
|
-
tailscale: "Tailscale Funnel",
|
|
10709
|
-
cpolar: "cpolar",
|
|
10710
|
-
cloudflared: "cloudflared",
|
|
10711
|
-
frp: t("frpName"),
|
|
10712
|
-
origin: t("originName")
|
|
10713
|
-
};
|
|
10714
|
-
const providerId = typeof facts.provider === "string" ? facts.provider : "";
|
|
10715
|
-
const controllerKey = typeof facts.controllerCode === "string" ? REMOTE_ERROR_MESSAGE_KEYS[facts.controllerCode] : void 0;
|
|
10716
|
-
const values = {
|
|
10717
|
-
provider: providerNames[providerId] ?? "",
|
|
10718
|
-
latencyMs: typeof facts.latencyMs === "number" && Number.isFinite(facts.latencyMs) ? new Intl.NumberFormat(localeTag).format(facts.latencyMs) : "",
|
|
10719
|
-
interfaceName: typeof facts.interfaceName === "string" ? facts.interfaceName : "",
|
|
10720
|
-
endpointSuffix: typeof facts.endpointSuffix === "string" ? facts.endpointSuffix : "",
|
|
10721
|
-
controllerCode: controllerKey === void 0 ? "" : t(controllerKey)
|
|
10722
|
-
};
|
|
10723
|
-
const interpolate = (template) => template.replace(/\{(\w+)\}/gu, (_match, key) => values[key] ?? "");
|
|
10724
|
-
let detail = interpolate(templates[0]);
|
|
10725
|
-
let action = interpolate(templates[1]);
|
|
10726
|
-
if (reason === "versions-current") {
|
|
10727
|
-
const versions = data.versions !== null && typeof data.versions === "object" ? data.versions : {};
|
|
10728
|
-
if ([
|
|
10729
|
-
versions.plugin,
|
|
10730
|
-
versions.dsh,
|
|
10731
|
-
versions.minimumAndroidApp
|
|
10732
|
-
].every((value) => typeof value === "string")) detail += ` plugin ${String(versions.plugin)}, DSH ${String(versions.dsh)}, Android ${String(versions.minimumAndroidApp)}.`;
|
|
10733
|
-
}
|
|
10734
|
-
if (reason === "remote-controller-error") action = diagnosticControllerAction(facts.controllerCode, locale, action);
|
|
10735
|
-
return {
|
|
10736
|
-
detail,
|
|
10737
|
-
action
|
|
10738
|
-
};
|
|
10739
|
-
};
|
|
10751
|
+
const localizedEntryCopy = (entry) => localizedDiagnosticEntryCopy(entry, locale, data.versions);
|
|
10740
10752
|
const appendGroup = (label, groupEntries) => {
|
|
10741
10753
|
if (groupEntries.length === 0) return;
|
|
10742
10754
|
const group = element("section", "dsh-mobile-control__diagnostic-group");
|
|
@@ -12368,6 +12380,7 @@ window.__ModuleLoader__.load({
|
|
|
12368
12380
|
exports.installCustomAssets = installCustomAssets;
|
|
12369
12381
|
exports.installDshLanguageBoundSurface = installDshLanguageBoundSurface;
|
|
12370
12382
|
exports.installMobileNativeBack = installMobileNativeBack;
|
|
12383
|
+
exports.localizedDiagnosticEntryCopy = localizedDiagnosticEntryCopy;
|
|
12371
12384
|
exports.mobileBackBlockedMessage = mobileBackBlockedMessage;
|
|
12372
12385
|
exports.normalizeDiagnosticOverall = normalizeDiagnosticOverall;
|
|
12373
12386
|
exports.normalizeDiagnosticStatus = normalizeDiagnosticStatus;
|