@dsh-remote/relay 3.1.9 → 3.2.1
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/README.md +16 -15
- package/deploy/docker/compose.yaml +12 -0
- package/dist/bundle/main.js +30 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,21 +18,22 @@ curl -s http://127.0.0.1:8787/healthz
|
|
|
18
18
|
|
|
19
19
|
### 常用配置
|
|
20
20
|
|
|
21
|
-
| 变量 | 必填 | 默认 | 说明
|
|
22
|
-
| ----------------------------- | ---- | ----------- |
|
|
23
|
-
| `DRC_HOST_TOKEN` | ✅ | — | 主机出站认证凭据。`openssl rand -hex 32` 生成。**与主机插件那份必须逐字一致**
|
|
24
|
-
| `DRC_PORT` | | `8787` | 监听端口。`0` = 让系统分配(测试与容器用)
|
|
25
|
-
| `DRC_BIND` | | `127.0.0.1` | 绑定地址。默认只绑回环:TLS 由反代终止。**只有自己就是边缘(容器直接发布端口)时才设 `0.0.0.0`**
|
|
26
|
-
| `DRC_PUBLIC_URL` | | 空 | `/api/info` 返回的对外地址。公网填 `wss://你的域名`
|
|
27
|
-
| `DRC_LOG_LEVEL` | | `info` | `debug` / `info` / `warn` / `error` / `silent`。**区分大小写**
|
|
28
|
-
| `DRC_PAIR_TTL_MS` | | `120000` | 配对码的**服务端权威**寿命(毫秒)
|
|
29
|
-
| `DRC_MP_APPID` | | — | 小程序 appid。与 `DRC_MP_SECRET` 同时配置才启用 `GET /code`(生成小程序码);不配则插件回退旧 `dshr:` 码
|
|
30
|
-
| `DRC_MP_SECRET` | | — | 小程序 secret。**只放在中继**,永不进插件包;换了 secret 记得同步这里
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
21
|
+
| 变量 | 必填 | 默认 | 说明 |
|
|
22
|
+
| ----------------------------- | ---- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| `DRC_HOST_TOKEN` | ✅ | — | 主机出站认证凭据。`openssl rand -hex 32` 生成。**与主机插件那份必须逐字一致** |
|
|
24
|
+
| `DRC_PORT` | | `8787` | 监听端口。`0` = 让系统分配(测试与容器用) |
|
|
25
|
+
| `DRC_BIND` | | `127.0.0.1` | 绑定地址。默认只绑回环:TLS 由反代终止。**只有自己就是边缘(容器直接发布端口)时才设 `0.0.0.0`** |
|
|
26
|
+
| `DRC_PUBLIC_URL` | | 空 | `/api/info` 返回的对外地址。公网填 `wss://你的域名` |
|
|
27
|
+
| `DRC_LOG_LEVEL` | | `info` | `debug` / `info` / `warn` / `error` / `silent`。**区分大小写** |
|
|
28
|
+
| `DRC_PAIR_TTL_MS` | | `120000` | 配对码的**服务端权威**寿命(毫秒) |
|
|
29
|
+
| `DRC_MP_APPID` | | — | 小程序 appid。与 `DRC_MP_SECRET` 同时配置才启用 `GET /code`(生成小程序码);不配则插件回退旧 `dshr:` 码 |
|
|
30
|
+
| `DRC_MP_SECRET` | | — | 小程序 secret。**只放在中继**,永不进插件包;换了 secret 记得同步这里 |
|
|
31
|
+
| `DRC_MP_ENV` | | `release` | 小程序码打开哪一版:`release` / `trial` / `develop`。**新版只上了体验版时必须设 `trial`**,否则码把用户带进老版本(能扫进去、配对必失败) |
|
|
32
|
+
| `DRC_STATE_FILE` | | 空(关闭) | 会话表落盘路径。默认关闭 = 纯内存;配上后启动先读它、会话表有增删时写回 |
|
|
33
|
+
| `DRC_STATE_SAVE_MS` | | `60000` | 周期补写状态文件的间隔(只为刷新 `lastActivityAt`) |
|
|
34
|
+
| `DRC_MAX_BUFFERED_BYTES` | | `1048576` | 慢消费者阈值:发送缓冲区**连续**超限达**本角色窗口**才 1008 断开 |
|
|
35
|
+
| `DRC_SLOW_CONSUMER_HOST_MS` | | `10000` | **主机**侧慢消费者窗口(毫秒)。主机卡住就是所有人卡住,所以窗口短 |
|
|
36
|
+
| `DRC_SLOW_CONSUMER_CLIENT_MS` | | `45000` | **客户端**侧慢消费者窗口(毫秒)。**必须大于手机自己的重连周期**(≈42.5 秒) |
|
|
36
37
|
|
|
37
38
|
### 端点
|
|
38
39
|
|
|
@@ -66,6 +66,18 @@ services:
|
|
|
66
66
|
DRC_MAX_CONNS: ${DRC_MAX_CONNS:-200}
|
|
67
67
|
DRC_CONV_IDLE_TTL_MS: ${DRC_CONV_IDLE_TTL_MS:-604800000}
|
|
68
68
|
DRC_HOST_GRACE_MS: ${DRC_HOST_GRACE_MS:-120000}
|
|
69
|
+
# 小程序码签发(2026-10-21「双码合一」):**只有中继持有小程序的 appSecret**,
|
|
70
|
+
# 主机插件那份永远拿不到它。两个都配了才启用 `GET /code?token=`;
|
|
71
|
+
# 不配(自部署常态)那条路由回 501,主机自动回落到旧 `dshr:` 码。
|
|
72
|
+
#
|
|
73
|
+
# ⚠️ **必须显式转发**:compose 只把这里列出的变量送进容器,
|
|
74
|
+
# 写在 `deploy/docker/.env` 里而这一行没有 ⇒ 进程读到的是 undefined ⇒ 501,
|
|
75
|
+
# 而日志一字不提(2026-10-21 上线当场踩到:healthz 已经 3.1.9、/code 仍 501)。
|
|
76
|
+
DRC_MP_APPID: ${DRC_MP_APPID:-}
|
|
77
|
+
DRC_MP_SECRET: ${DRC_MP_SECRET:-}
|
|
78
|
+
# 码打开哪一版:release / trial / develop(缺省 release)。
|
|
79
|
+
# 新版只上了体验版时必须设 trial,否则码把用户带进老版本:能扫进去、配对必失败。
|
|
80
|
+
DRC_MP_ENV: ${DRC_MP_ENV:-}
|
|
69
81
|
|
|
70
82
|
volumes:
|
|
71
83
|
- ${DRC_DATA_DIR:-relay-data}:/data
|
package/dist/bundle/main.js
CHANGED
|
@@ -23443,7 +23443,7 @@ function date4(params) {
|
|
|
23443
23443
|
return _coercedDate(ZodDate, params);
|
|
23444
23444
|
}
|
|
23445
23445
|
|
|
23446
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
23446
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/limits.js
|
|
23447
23447
|
var MAX_RELAY_MESSAGE_BYTES = 1024 * 1024;
|
|
23448
23448
|
var MAX_CIPHERTEXT_BYTES = MAX_RELAY_MESSAGE_BYTES - 8 * 1024;
|
|
23449
23449
|
var MAX_IMAGE_ATTACHMENTS = 4;
|
|
@@ -23452,7 +23452,7 @@ var MAX_ATTACHMENT_BYTES = 512 * 1024;
|
|
|
23452
23452
|
var MAX_ATTACHMENT_TOTAL_BYTES = 512 * 1024;
|
|
23453
23453
|
var IDEMPOTENCY_WINDOW_MS = 5 * 60 * 1e3;
|
|
23454
23454
|
|
|
23455
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
23455
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/frames.js
|
|
23456
23456
|
var PROTOCOL_VERSION = 1;
|
|
23457
23457
|
var PAIRING_TOKEN_RE = /^\d{6}$/;
|
|
23458
23458
|
var nonEmpty = external_exports.string().min(1);
|
|
@@ -23878,6 +23878,7 @@ function loadConfig(env, version2) {
|
|
|
23878
23878
|
hostToken,
|
|
23879
23879
|
mpAppId: (env.DRC_MP_APPID ?? "").trim(),
|
|
23880
23880
|
mpSecret: (env.DRC_MP_SECRET ?? "").trim(),
|
|
23881
|
+
mpEnv: (env.DRC_MP_ENV ?? "").trim(),
|
|
23881
23882
|
port: portNumber(env.DRC_PORT, 8787, "DRC_PORT", problems),
|
|
23882
23883
|
// 默认只绑回环:TLS 由反代终止,中继没理由出现在局域网里。
|
|
23883
23884
|
// 只有自己就是边缘(容器直接发布端口)时才设 DRC_BIND=0.0.0.0。
|
|
@@ -23964,7 +23965,7 @@ var import_subprotocol = __toESM(require_subprotocol(), 1);
|
|
|
23964
23965
|
var import_websocket = __toESM(require_websocket(), 1);
|
|
23965
23966
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
23966
23967
|
|
|
23967
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
23968
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/identity/ids.js
|
|
23968
23969
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
23969
23970
|
var CONVERSATION_ID_PREFIX = "c_";
|
|
23970
23971
|
function newConversationId() {
|
|
@@ -23974,7 +23975,7 @@ function newHostId() {
|
|
|
23974
23975
|
return randomBytes(4).toString("hex");
|
|
23975
23976
|
}
|
|
23976
23977
|
|
|
23977
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
23978
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/negotiate.js
|
|
23978
23979
|
function negotiateProtocol(peerProtocol, own2 = PROTOCOL_VERSION) {
|
|
23979
23980
|
if (peerProtocol === void 0 || peerProtocol === null) {
|
|
23980
23981
|
return {
|
|
@@ -24016,9 +24017,15 @@ var CODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit";
|
|
|
24016
24017
|
var LANDING_PAGE = "pages/sessions/sessions";
|
|
24017
24018
|
var TOKEN_SKEW_MS = 6e4;
|
|
24018
24019
|
var DEFAULT_TOKEN_TTL_MS = 7e6;
|
|
24020
|
+
var ENV_VERSIONS = ["release", "trial", "develop"];
|
|
24021
|
+
function normalizeEnvVersion(value) {
|
|
24022
|
+
const normalized = (value ?? "").trim().toLowerCase();
|
|
24023
|
+
return ENV_VERSIONS.includes(normalized) ? normalized : "release";
|
|
24024
|
+
}
|
|
24019
24025
|
function createMpCodeIssuer(options) {
|
|
24020
24026
|
const appId = options.appId.trim();
|
|
24021
24027
|
const secret = options.secret.trim();
|
|
24028
|
+
const envVersion = normalizeEnvVersion(options.envVersion);
|
|
24022
24029
|
const doFetch = options.fetch ?? ((...args) => fetch(...args));
|
|
24023
24030
|
const now = options.now ?? (() => Date.now());
|
|
24024
24031
|
let cachedToken;
|
|
@@ -24043,7 +24050,17 @@ function createMpCodeIssuer(options) {
|
|
|
24043
24050
|
const response = await doFetch(`${CODE_URL}?access_token=${encodeURIComponent(accessToken)}`, {
|
|
24044
24051
|
method: "POST",
|
|
24045
24052
|
headers: { "content-type": "application/json" },
|
|
24046
|
-
body: JSON.stringify({
|
|
24053
|
+
body: JSON.stringify({
|
|
24054
|
+
scene: pairingToken2,
|
|
24055
|
+
page: LANDING_PAGE,
|
|
24056
|
+
/**
|
|
24057
|
+
* `check_path: false`(默认 true):true 时 `page` 必须是**已发布版本里存在**的页面。
|
|
24058
|
+
* 3.2 的首页路径没变,但排错期(`env_version=trial/develop`)里那份页面可能还没发布,
|
|
24059
|
+
* 开着它就会得到 41030 invalid page —— 而症状只是"码生成失败",看不出是版本的事。
|
|
24060
|
+
*/
|
|
24061
|
+
check_path: false,
|
|
24062
|
+
env_version: envVersion
|
|
24063
|
+
})
|
|
24047
24064
|
});
|
|
24048
24065
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
24049
24066
|
if (bytes.length > 2 && bytes[0] === 255 && bytes[1] === 216) return bytes;
|
|
@@ -24078,16 +24095,16 @@ function createMpCodeIssuer(options) {
|
|
|
24078
24095
|
};
|
|
24079
24096
|
}
|
|
24080
24097
|
|
|
24081
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
24098
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/errors.js
|
|
24082
24099
|
var WAIT_HINTABLE = /* @__PURE__ */ new Set(["rate_limited", "pair_table_full"]);
|
|
24083
24100
|
function wantsRetryAfter(code) {
|
|
24084
24101
|
return typeof code === "string" && WAIT_HINTABLE.has(code);
|
|
24085
24102
|
}
|
|
24086
24103
|
|
|
24087
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
24104
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/platform/sleep.js
|
|
24088
24105
|
var SLEEP_BACKENDS = ["caffeinate", "systemd-inhibit", "unsupported"];
|
|
24089
24106
|
|
|
24090
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
24107
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/payloads.js
|
|
24091
24108
|
var SESSION_STATES = [
|
|
24092
24109
|
"idle",
|
|
24093
24110
|
"running",
|
|
@@ -24513,7 +24530,7 @@ var PAYLOAD_TYPES = {
|
|
|
24513
24530
|
evSessionHistory: "ev.session_history"
|
|
24514
24531
|
};
|
|
24515
24532
|
|
|
24516
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
24533
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/outbound.js
|
|
24517
24534
|
function helloOkForClient(clientId, protocol) {
|
|
24518
24535
|
return { t: "hello-ok", role: "client", clientId, ...protocol === void 0 ? {} : { protocol } };
|
|
24519
24536
|
}
|
|
@@ -24600,7 +24617,7 @@ function encBatchToRelay(sessionId, items) {
|
|
|
24600
24617
|
return { t: "enc-batch", sessionId, items: items.map((item) => ({ ...item })) };
|
|
24601
24618
|
}
|
|
24602
24619
|
|
|
24603
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
24620
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/registry.js
|
|
24604
24621
|
var ENDPOINT_FRAME_NAMES = [
|
|
24605
24622
|
"hello",
|
|
24606
24623
|
"pair-begin",
|
|
@@ -24665,7 +24682,7 @@ var EV_TYPES = [
|
|
|
24665
24682
|
];
|
|
24666
24683
|
var PAYLOAD_TYPE_SET = /* @__PURE__ */ new Set([...CMD_TYPES, ...EV_TYPES]);
|
|
24667
24684
|
|
|
24668
|
-
// node_modules/.pnpm/@dsh-remote+protocol@3.
|
|
24685
|
+
// node_modules/.pnpm/@dsh-remote+protocol@3.2.0/node_modules/@dsh-remote/protocol/dist/src/wire/classify.js
|
|
24669
24686
|
function frameTypeOf(value) {
|
|
24670
24687
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
24671
24688
|
return void 0;
|
|
@@ -26281,7 +26298,7 @@ function createRelay(config2) {
|
|
|
26281
26298
|
}
|
|
26282
26299
|
}
|
|
26283
26300
|
}
|
|
26284
|
-
const mpCodes = createMpCodeIssuer({ appId: config2.mpAppId, secret: config2.mpSecret });
|
|
26301
|
+
const mpCodes = createMpCodeIssuer({ appId: config2.mpAppId, secret: config2.mpSecret, envVersion: config2.mpEnv });
|
|
26285
26302
|
const http = createServer(httpHandler);
|
|
26286
26303
|
http.on("error", (error62) => {
|
|
26287
26304
|
log.warn("http server error", { message: String(error62?.message ?? error62) });
|
|
@@ -26433,7 +26450,7 @@ function registerSignalHandlers(shutdown) {
|
|
|
26433
26450
|
|
|
26434
26451
|
// src/main.ts
|
|
26435
26452
|
function readVersion() {
|
|
26436
|
-
if ("3.1
|
|
26453
|
+
if ("3.2.1") return "3.2.1";
|
|
26437
26454
|
try {
|
|
26438
26455
|
const pkg = JSON.parse(readFileSync2(new URL("../../package.json", import.meta.url), "utf8"));
|
|
26439
26456
|
if (typeof pkg.version === "string") return pkg.version;
|