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/lib/index.mjs
CHANGED
|
@@ -10610,6 +10610,8 @@ var RemoteProviderCoordinator = class {
|
|
|
10610
10610
|
store;
|
|
10611
10611
|
selectedValue;
|
|
10612
10612
|
queue = Promise.resolve();
|
|
10613
|
+
closing = false;
|
|
10614
|
+
closeTask;
|
|
10613
10615
|
constructor(selected, controllers, store) {
|
|
10614
10616
|
this.controllers = controllers;
|
|
10615
10617
|
this.store = store;
|
|
@@ -10661,8 +10663,16 @@ var RemoteProviderCoordinator = class {
|
|
|
10661
10663
|
}
|
|
10662
10664
|
});
|
|
10663
10665
|
}
|
|
10666
|
+
/** Stop admitting mutations and wait for accepted work before callers release provider resources. */
|
|
10667
|
+
close() {
|
|
10668
|
+
this.closing = true;
|
|
10669
|
+
this.closeTask ??= this.queue;
|
|
10670
|
+
return this.closeTask;
|
|
10671
|
+
}
|
|
10664
10672
|
enqueue(operation) {
|
|
10665
|
-
|
|
10673
|
+
if (this.closing) return Promise.reject(/* @__PURE__ */ new Error("remote_provider_closed"));
|
|
10674
|
+
const run = () => this.closing ? Promise.reject(/* @__PURE__ */ new Error("remote_provider_closed")) : this.runAndEnforce(operation);
|
|
10675
|
+
const task = this.queue.then(run, run);
|
|
10666
10676
|
this.queue = task.then(() => void 0, () => void 0);
|
|
10667
10677
|
return task;
|
|
10668
10678
|
}
|
|
@@ -13353,6 +13363,8 @@ const MOBILE_CUSTOMIZATION_GUIDE_BODY = `你在为用户定制 DSH Mobile 的手
|
|
|
13353
13363
|
|
|
13354
13364
|
动手前先读已有的定制文件。若要调整 DSH 自带的输入框、侧栏或设置等现有界面,先确认当前 DSH 版本与手机页面模式(专用移动页面或 ?frontend=stock),只读核对运行页面的 DOM、属性和样式;本机有同版本前端源码时可参考相应组件,没有源码时可查看已安装的客户端产物,不必下载源码。优先使用扩展 API、插槽和稳定的 data-* 标记,不凭猜测依赖构建类名。只读查看不改变前述写入范围。
|
|
13355
13365
|
|
|
13366
|
+
旧 CSS 可能覆盖新版内置修复。先备份并在同一会话、方向和字号下对比有无该样式,不要无症状叠加旧补丁。CSS Modules 的后缀/子串匹配必须限定到已核对的区域祖先,例如 [data-dsh-mobile-header] [class*="_titleRow"];不要裸匹配整页。display: none 会让其中的操作不可用,不得把隐藏功能描述成折叠。
|
|
13367
|
+
|
|
13356
13368
|
2. 电脑端能力 —— 手机需要读电脑文件、执行命令或访问硬件时,创建扩展:
|
|
13357
13369
|
- 目录:$DSH_HOME/mobile-access/extensions/<id>/,id 用小写字母数字和连字符(如 media-remote)
|
|
13358
13370
|
- extension.json:{"schemaVersion":1,"id":"<id>","name":"显示名","version":"0.1.0","description":"说明"}
|
|
@@ -13365,6 +13377,8 @@ const MOBILE_CUSTOMIZATION_GUIDE_BODY = `你在为用户定制 DSH Mobile 的手
|
|
|
13365
13377
|
- mobile.js 里用 api.host.invoke('动作名', 输入) 调 host.mjs 的 action(请求会按 application/json 发送),api.host.fetch('/路由路径') 调 route,api.host.assetUrl('相对路径') 生成与当前版本绑定的资源地址
|
|
13366
13378
|
- DSH Web 可先用命令生成模板:dsh plugin --profile web exec dsh-mobile extension create <id> --name "<名称>",再在模板上改;Desktop 请在上方实际定制目录创建文件,不要误改 Web profile
|
|
13367
13379
|
|
|
13380
|
+
附件功能优先复用 DSH 自带的文件上传。扩展若需向输入框模拟文件拖放,dataTransfer 除了 files,还须有可迭代的 items(DataTransferItemList),文件项应提供 kind、type 和 getAsFile();不能只拼一个 files 数组。原生 DataTransfer / DragEvent 可用时优先使用,并通过 items.add(file) 填充文件;还要验证 effectAllowed / dropEffect 能否按投递逻辑读写,不能只检查构造函数是否存在。不完整的 WebView 需要保留这些能力的兼容实现。dragover 接受或事件派发成功不代表附件已加入,须检查 DSH 输入框草稿中的附件与解码后的图片预览;不要用电脑端附件目录的文件数量或修改时间代替这项验证。
|
|
13381
|
+
|
|
13368
13382
|
安全约束:
|
|
13369
13383
|
- host.mjs 拥有电脑用户的完整权限,绝不能放入不可信代码,也不要让手机端无条件执行任意命令
|
|
13370
13384
|
- in-process 的同步阻塞会卡住网关事件循环,超时不能打断;Worker 可约束 JavaScript 阻塞,但不是安全沙箱,不约束全部原生内存,也不回滚副作用。两种模式都应响应 signal,避免编写不可取消的同步循环
|
|
@@ -14810,7 +14824,17 @@ var FunnelController = class {
|
|
|
14810
14824
|
}
|
|
14811
14825
|
async failGeneration(generation, code, setupUrl) {
|
|
14812
14826
|
if (generation !== this.generation) return;
|
|
14813
|
-
|
|
14827
|
+
++this.generation;
|
|
14828
|
+
try {
|
|
14829
|
+
await this.stopProcessAndGateway();
|
|
14830
|
+
} catch (_error) {
|
|
14831
|
+
this.publish({
|
|
14832
|
+
enabled: this.enabled,
|
|
14833
|
+
state: "error",
|
|
14834
|
+
errorCode: "funnel_cleanup_failed"
|
|
14835
|
+
});
|
|
14836
|
+
return;
|
|
14837
|
+
}
|
|
14814
14838
|
if (this.enabled) this.publish({
|
|
14815
14839
|
enabled: true,
|
|
14816
14840
|
state: "error",
|
|
@@ -15295,10 +15319,35 @@ async function removeCaddyTree(root, target) {
|
|
|
15295
15319
|
}
|
|
15296
15320
|
//#endregion
|
|
15297
15321
|
//#region src/caddy-component.ts
|
|
15298
|
-
const CADDY_VERSION = "2.11.
|
|
15322
|
+
const CADDY_VERSION = "2.11.7";
|
|
15299
15323
|
const CADDY_DNS_PLUGIN_VERSION = "v0.4.3";
|
|
15300
|
-
const CADDY_DOWNLOAD_PAGE = "https://
|
|
15301
|
-
const CADDY_COMPONENT_RELEASES = Object.freeze({
|
|
15324
|
+
const CADDY_DOWNLOAD_PAGE = "https://github.com/saya-ch/dsh-mobile/releases/tag/caddy-component-2.11.7-tencentcloud-0.4.3-r1";
|
|
15325
|
+
const CADDY_COMPONENT_RELEASES = Object.freeze({
|
|
15326
|
+
"linux-x64": Object.freeze({
|
|
15327
|
+
version: CADDY_VERSION,
|
|
15328
|
+
dnsPluginVersion: CADDY_DNS_PLUGIN_VERSION,
|
|
15329
|
+
platform: "linux",
|
|
15330
|
+
arch: "x64",
|
|
15331
|
+
executableName: "caddy",
|
|
15332
|
+
downloadUrl: "https://github.com/saya-ch/dsh-mobile/releases/download/caddy-component-2.11.7-tencentcloud-0.4.3-r1/caddy-2.11.7-tencentcloud-0.4.3-linux-x64",
|
|
15333
|
+
downloadBytes: 81995704,
|
|
15334
|
+
executableBytes: 81995704,
|
|
15335
|
+
downloadSha256: "13652ef9ea3792ebff772a5b873ade63ed6de1c0a794bb3403a523639b8f57af",
|
|
15336
|
+
executableSha256: "13652ef9ea3792ebff772a5b873ade63ed6de1c0a794bb3403a523639b8f57af"
|
|
15337
|
+
}),
|
|
15338
|
+
"win32-x64": Object.freeze({
|
|
15339
|
+
version: CADDY_VERSION,
|
|
15340
|
+
dnsPluginVersion: CADDY_DNS_PLUGIN_VERSION,
|
|
15341
|
+
platform: "win32",
|
|
15342
|
+
arch: "x64",
|
|
15343
|
+
executableName: "caddy.exe",
|
|
15344
|
+
downloadUrl: "https://github.com/saya-ch/dsh-mobile/releases/download/caddy-component-2.11.7-tencentcloud-0.4.3-r1/caddy-2.11.7-tencentcloud-0.4.3-win32-x64.exe",
|
|
15345
|
+
downloadBytes: 82816e3,
|
|
15346
|
+
executableBytes: 82816e3,
|
|
15347
|
+
downloadSha256: "962f7937a06e1e737513d2d1d4ad75df54ba503d36fa2a458912bd38fbd0094a",
|
|
15348
|
+
executableSha256: "962f7937a06e1e737513d2d1d4ad75df54ba503d36fa2a458912bd38fbd0094a"
|
|
15349
|
+
})
|
|
15350
|
+
});
|
|
15302
15351
|
Object.freeze({
|
|
15303
15352
|
version: CADDY_VERSION,
|
|
15304
15353
|
dnsPluginVersion: CADDY_DNS_PLUGIN_VERSION
|
|
@@ -15322,14 +15371,16 @@ function caddyInspectionEnvironment(environment = process.env) {
|
|
|
15322
15371
|
]) if (environment[name] !== void 0) result[name] = environment[name];
|
|
15323
15372
|
return result;
|
|
15324
15373
|
}
|
|
15325
|
-
async function inspect(executable) {
|
|
15374
|
+
async function inspect(executable, assertOpen) {
|
|
15326
15375
|
const options = {
|
|
15327
15376
|
env: caddyInspectionEnvironment(),
|
|
15328
15377
|
timeout: 15e3,
|
|
15329
15378
|
maxBuffer: 262144
|
|
15330
15379
|
};
|
|
15380
|
+
const version = (await execFileText(executable, ["version"], options)).stdout.trim();
|
|
15381
|
+
assertOpen();
|
|
15331
15382
|
return {
|
|
15332
|
-
version
|
|
15383
|
+
version,
|
|
15333
15384
|
modules: (await execFileText(executable, ["list-modules", "--versions"], options)).stdout
|
|
15334
15385
|
};
|
|
15335
15386
|
}
|
|
@@ -15348,6 +15399,10 @@ var CaddyComponentManager = class {
|
|
|
15348
15399
|
installed = false;
|
|
15349
15400
|
errorCode;
|
|
15350
15401
|
queue = Promise.resolve();
|
|
15402
|
+
closed = false;
|
|
15403
|
+
closing;
|
|
15404
|
+
download;
|
|
15405
|
+
cleanupPaths = /* @__PURE__ */ new Set();
|
|
15351
15406
|
constructor(options) {
|
|
15352
15407
|
if (!isAbsolute(options.stateDirectory)) throw new Error("caddy state directory must be absolute");
|
|
15353
15408
|
this.stateDirectory = resolve(options.stateDirectory);
|
|
@@ -15357,7 +15412,7 @@ var CaddyComponentManager = class {
|
|
|
15357
15412
|
if (artifact !== void 0 && (artifact.platform !== platform || artifact.arch !== arch || !/^https:\/\//u.test(artifact.downloadUrl) || !/^[a-f0-9]{64}$/u.test(artifact.downloadSha256) || !/^[a-f0-9]{64}$/u.test(artifact.executableSha256) || artifact.downloadBytes < 1 || artifact.executableBytes < 1 || !/^[a-z0-9.-]+$/u.test(artifact.executableName))) throw new Error("caddy_artifact_invalid");
|
|
15358
15413
|
this.artifact = artifact;
|
|
15359
15414
|
this.componentRoot = join(this.stateDirectory, "components", "caddy");
|
|
15360
|
-
this.componentStorage = join(this.componentRoot, artifact?.version ?? "2.11.
|
|
15415
|
+
this.componentStorage = join(this.componentRoot, artifact?.version ?? "2.11.7");
|
|
15361
15416
|
this.executable = join(this.componentStorage, artifact?.executableName ?? (platform === "win32" ? "caddy.exe" : "caddy"));
|
|
15362
15417
|
this.logRoot = join(this.stateDirectory, "caddy", "logs");
|
|
15363
15418
|
this.stagingRoot = join(this.stateDirectory, "staging", "caddy");
|
|
@@ -15367,41 +15422,48 @@ var CaddyComponentManager = class {
|
|
|
15367
15422
|
expectedBytes: artifact?.downloadBytes ?? 0,
|
|
15368
15423
|
errorPrefix: "caddy"
|
|
15369
15424
|
}));
|
|
15370
|
-
this.inspectExecutable = options.inspectExecutable ?? inspect;
|
|
15425
|
+
this.inspectExecutable = options.inspectExecutable ?? ((executable) => inspect(executable, () => this.assertOpen()));
|
|
15371
15426
|
this.promote = options.promoteDirectory ?? ((source, destination) => renameCaddyPath(this.stateDirectory, source, destination));
|
|
15372
15427
|
}
|
|
15373
15428
|
/** Recheck a pinned executable before launching; no unverified local binary can run. */
|
|
15374
|
-
|
|
15429
|
+
ensureExecutable() {
|
|
15430
|
+
return this.enqueue(() => this.checkExecutable());
|
|
15431
|
+
}
|
|
15432
|
+
async checkExecutable() {
|
|
15375
15433
|
const artifact = this.artifact;
|
|
15376
15434
|
if (artifact === void 0) throw new Error("caddy_component_unavailable");
|
|
15377
15435
|
await assertCaddyParents(this.stateDirectory, this.executable);
|
|
15436
|
+
this.assertOpen();
|
|
15378
15437
|
const entry = await lstat(this.executable);
|
|
15379
15438
|
if (!entry.isFile() || entry.isSymbolicLink() || entry.size !== artifact.executableBytes || await digest(this.executable) !== artifact.executableSha256) throw new Error("caddy_component_invalid");
|
|
15380
15439
|
await this.verifyMetadata(this.executable, artifact);
|
|
15381
15440
|
}
|
|
15382
15441
|
async initialize() {
|
|
15383
|
-
this.
|
|
15384
|
-
|
|
15385
|
-
|
|
15386
|
-
this.
|
|
15387
|
-
|
|
15388
|
-
|
|
15389
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15442
|
+
return this.enqueue(async () => {
|
|
15443
|
+
this.installed = false;
|
|
15444
|
+
this.errorCode = void 0;
|
|
15445
|
+
if (this.artifact === void 0) {
|
|
15446
|
+
this.errorCode = "caddy_component_unavailable";
|
|
15447
|
+
return;
|
|
15448
|
+
}
|
|
15449
|
+
try {
|
|
15450
|
+
await this.checkExecutable();
|
|
15451
|
+
this.installed = true;
|
|
15452
|
+
} catch (error) {
|
|
15453
|
+
this.assertOpen();
|
|
15454
|
+
if (error.code !== "ENOENT") this.errorCode = "caddy_component_invalid";
|
|
15455
|
+
}
|
|
15456
|
+
});
|
|
15395
15457
|
}
|
|
15396
15458
|
status() {
|
|
15397
15459
|
const artifact = this.artifact;
|
|
15398
15460
|
return Object.freeze({
|
|
15399
15461
|
supported: artifact !== void 0,
|
|
15400
15462
|
installed: this.installed,
|
|
15401
|
-
version: artifact?.version ?? "2.11.
|
|
15463
|
+
version: artifact?.version ?? "2.11.7",
|
|
15402
15464
|
downloadBytes: artifact?.downloadBytes ?? 0,
|
|
15403
15465
|
installedBytes: this.installed ? artifact?.executableBytes ?? 0 : 0,
|
|
15404
|
-
sourceUrl: artifact?.downloadUrl ?? "https://
|
|
15466
|
+
sourceUrl: artifact?.downloadUrl ?? "https://github.com/saya-ch/dsh-mobile/releases/tag/caddy-component-2.11.7-tencentcloud-0.4.3-r1",
|
|
15405
15467
|
downloadPage: CADDY_DOWNLOAD_PAGE,
|
|
15406
15468
|
storagePath: this.componentRoot,
|
|
15407
15469
|
...this.errorCode === void 0 ? {} : { errorCode: this.errorCode }
|
|
@@ -15413,28 +15475,40 @@ var CaddyComponentManager = class {
|
|
|
15413
15475
|
const artifact = this.artifact;
|
|
15414
15476
|
if (artifact === void 0) throw new Error("caddy_component_unavailable");
|
|
15415
15477
|
await ensureCaddyDirectory(this.stateDirectory, this.stagingRoot);
|
|
15478
|
+
this.assertOpen();
|
|
15416
15479
|
await ensureCaddyDirectory(this.stateDirectory, this.componentRoot);
|
|
15480
|
+
this.assertOpen();
|
|
15417
15481
|
const staging = await mkdtemp(join(this.stagingRoot, "install-"));
|
|
15418
15482
|
const candidate = join(this.componentRoot, ".install-" + randomBytes(12).toString("hex"));
|
|
15419
15483
|
const backup = join(this.componentRoot, ".previous-" + randomBytes(12).toString("hex"));
|
|
15420
15484
|
let previous = false;
|
|
15421
15485
|
let promoted = false;
|
|
15422
15486
|
let failed = false;
|
|
15487
|
+
let operationError;
|
|
15423
15488
|
try {
|
|
15489
|
+
this.assertOpen();
|
|
15424
15490
|
const controller = new AbortController();
|
|
15425
15491
|
const timeout = setTimeout(() => {
|
|
15426
15492
|
controller.abort();
|
|
15427
15493
|
}, 6e5);
|
|
15428
15494
|
timeout.unref();
|
|
15495
|
+
this.download = {
|
|
15496
|
+
controller,
|
|
15497
|
+
timeout
|
|
15498
|
+
};
|
|
15429
15499
|
let bytes;
|
|
15430
15500
|
try {
|
|
15431
15501
|
bytes = await this.fetchArtifact(artifact.downloadUrl, controller.signal);
|
|
15432
15502
|
} finally {
|
|
15433
15503
|
clearTimeout(timeout);
|
|
15504
|
+
this.download = void 0;
|
|
15434
15505
|
}
|
|
15506
|
+
this.assertOpen();
|
|
15507
|
+
controller.signal.throwIfAborted();
|
|
15435
15508
|
if (bytes.byteLength !== artifact.downloadBytes) throw new Error("caddy_download_size_mismatch");
|
|
15436
15509
|
if (createHash("sha256").update(bytes).digest("hex") !== artifact.downloadSha256) throw new Error("caddy_download_hash_mismatch");
|
|
15437
15510
|
await ensureCaddyDirectory(this.stateDirectory, candidate);
|
|
15511
|
+
this.assertOpen();
|
|
15438
15512
|
const executable = join(candidate, artifact.executableName);
|
|
15439
15513
|
await writeFile(executable, bytes, {
|
|
15440
15514
|
flag: "wx",
|
|
@@ -15443,8 +15517,10 @@ var CaddyComponentManager = class {
|
|
|
15443
15517
|
await chmod(executable, 448);
|
|
15444
15518
|
if (bytes.byteLength !== artifact.executableBytes || await digest(executable) !== artifact.executableSha256) throw new Error("caddy_executable_hash_mismatch");
|
|
15445
15519
|
await this.verifyMetadata(executable, artifact);
|
|
15520
|
+
this.assertOpen();
|
|
15446
15521
|
try {
|
|
15447
15522
|
const entry = await lstat(this.componentStorage);
|
|
15523
|
+
this.assertOpen();
|
|
15448
15524
|
if (!entry.isDirectory() || entry.isSymbolicLink()) throw new Error("caddy_path_invalid");
|
|
15449
15525
|
await renameCaddyPath(this.stateDirectory, this.componentStorage, backup);
|
|
15450
15526
|
previous = true;
|
|
@@ -15452,6 +15528,7 @@ var CaddyComponentManager = class {
|
|
|
15452
15528
|
if (error.code !== "ENOENT") throw error;
|
|
15453
15529
|
}
|
|
15454
15530
|
try {
|
|
15531
|
+
this.assertOpen();
|
|
15455
15532
|
await this.promote(candidate, this.componentStorage);
|
|
15456
15533
|
promoted = true;
|
|
15457
15534
|
} catch (error) {
|
|
@@ -15469,21 +15546,29 @@ var CaddyComponentManager = class {
|
|
|
15469
15546
|
this.errorCode = void 0;
|
|
15470
15547
|
} catch (error) {
|
|
15471
15548
|
failed = true;
|
|
15549
|
+
operationError = error;
|
|
15472
15550
|
throw error;
|
|
15473
15551
|
} finally {
|
|
15474
|
-
|
|
15552
|
+
const cleanupErrors = [];
|
|
15475
15553
|
for (const path of [
|
|
15476
15554
|
staging,
|
|
15477
15555
|
candidate,
|
|
15478
15556
|
...previous && promoted ? [backup] : []
|
|
15479
|
-
])
|
|
15480
|
-
|
|
15481
|
-
|
|
15482
|
-
|
|
15557
|
+
]) {
|
|
15558
|
+
this.cleanupPaths.add(path);
|
|
15559
|
+
try {
|
|
15560
|
+
await removeCaddyTree(this.stateDirectory, path);
|
|
15561
|
+
this.cleanupPaths.delete(path);
|
|
15562
|
+
} catch (error) {
|
|
15563
|
+
cleanupErrors.push(error);
|
|
15564
|
+
}
|
|
15565
|
+
}
|
|
15566
|
+
if (cleanupErrors.length > 0) {
|
|
15567
|
+
if (!failed && cleanupErrors.length === 1) throw cleanupErrors[0];
|
|
15568
|
+
throw new AggregateError([...failed ? [operationError] : [], ...cleanupErrors], "caddy_component_cleanup_failed", failed ? { cause: operationError } : void 0);
|
|
15483
15569
|
}
|
|
15484
|
-
if (!failed && cleanupError !== void 0) throw cleanupError;
|
|
15485
15570
|
}
|
|
15486
|
-
});
|
|
15571
|
+
}).then(() => this.status());
|
|
15487
15572
|
}
|
|
15488
15573
|
/** Remove optional component files; callers stop the controller and purge runtime configuration separately. */
|
|
15489
15574
|
purge() {
|
|
@@ -15492,18 +15577,57 @@ var CaddyComponentManager = class {
|
|
|
15492
15577
|
await removeCaddyTree(this.stateDirectory, this.stagingRoot);
|
|
15493
15578
|
this.installed = false;
|
|
15494
15579
|
this.errorCode = this.artifact === void 0 ? "caddy_component_unavailable" : void 0;
|
|
15580
|
+
}).then(() => this.status());
|
|
15581
|
+
}
|
|
15582
|
+
/**
|
|
15583
|
+
* Permanently stop admission and abort downloads, then await admitted work and cleanup.
|
|
15584
|
+
* An already started promotion/purge settles normally; cancellation does not undo committed bytes.
|
|
15585
|
+
* A downloader that ignores abort remains owned until its promise settles. Failed temporary cleanup
|
|
15586
|
+
* is retried here; a failed close can be called again without reopening admission or deleting recovery backups.
|
|
15587
|
+
*/
|
|
15588
|
+
close() {
|
|
15589
|
+
if (this.closing !== void 0) return this.closing;
|
|
15590
|
+
this.closed = true;
|
|
15591
|
+
if (this.download !== void 0) {
|
|
15592
|
+
clearTimeout(this.download.timeout);
|
|
15593
|
+
this.download.controller.abort(/* @__PURE__ */ new Error("caddy_component_closed"));
|
|
15594
|
+
}
|
|
15595
|
+
const task = this.queue.then(async () => {
|
|
15596
|
+
const errors = [];
|
|
15597
|
+
for (const path of this.cleanupPaths) try {
|
|
15598
|
+
await removeCaddyTree(this.stateDirectory, path);
|
|
15599
|
+
this.cleanupPaths.delete(path);
|
|
15600
|
+
} catch (error) {
|
|
15601
|
+
errors.push(error);
|
|
15602
|
+
}
|
|
15603
|
+
if (errors.length === 1) throw errors[0];
|
|
15604
|
+
if (errors.length > 1) throw new AggregateError(errors, "caddy_component_cleanup_failed");
|
|
15495
15605
|
});
|
|
15606
|
+
this.closing = task;
|
|
15607
|
+
task.catch(() => {
|
|
15608
|
+
if (this.closing === task) this.closing = void 0;
|
|
15609
|
+
});
|
|
15610
|
+
return task;
|
|
15496
15611
|
}
|
|
15497
15612
|
async verifyMetadata(executable, artifact) {
|
|
15613
|
+
this.assertOpen();
|
|
15498
15614
|
const info = await this.inspectExecutable(executable);
|
|
15615
|
+
this.assertOpen();
|
|
15499
15616
|
if (!info.version.startsWith("v" + artifact.version + " ")) throw new Error("caddy_executable_version_mismatch");
|
|
15500
15617
|
const nonstandard = info.modules.split(/\r?\n/u).filter((line) => line.startsWith("dns.providers."));
|
|
15501
15618
|
if (nonstandard.length !== 1 || nonstandard[0] !== "dns.providers.tencentcloud " + artifact.dnsPluginVersion || !info.modules.includes("Non-standard modules: 1")) throw new Error("caddy_executable_modules_mismatch");
|
|
15502
15619
|
}
|
|
15620
|
+
assertOpen() {
|
|
15621
|
+
if (this.closed) throw new Error("caddy_component_closed");
|
|
15622
|
+
}
|
|
15503
15623
|
enqueue(operation) {
|
|
15504
|
-
|
|
15624
|
+
if (this.closed) return Promise.reject(/* @__PURE__ */ new Error("caddy_component_closed"));
|
|
15625
|
+
const task = this.queue.then(() => {
|
|
15626
|
+
this.assertOpen();
|
|
15627
|
+
return operation();
|
|
15628
|
+
});
|
|
15505
15629
|
this.queue = task.then(() => void 0, () => void 0);
|
|
15506
|
-
return task
|
|
15630
|
+
return task;
|
|
15507
15631
|
}
|
|
15508
15632
|
};
|
|
15509
15633
|
const MAX_CONFIG_BYTES = 16384;
|
|
@@ -15862,6 +15986,7 @@ var CaddyController = class {
|
|
|
15862
15986
|
} catch {}
|
|
15863
15987
|
}
|
|
15864
15988
|
async start() {
|
|
15989
|
+
if (this.child !== void 0 || this.gatewayValue !== void 0) await this.stop();
|
|
15865
15990
|
const generation = ++this.generation;
|
|
15866
15991
|
const startup = new AbortController();
|
|
15867
15992
|
this.startupAbort = startup;
|
|
@@ -15911,12 +16036,19 @@ var CaddyController = class {
|
|
|
15911
16036
|
"--adapter",
|
|
15912
16037
|
"caddyfile"
|
|
15913
16038
|
], environment);
|
|
15914
|
-
|
|
16039
|
+
const ownedChild = {
|
|
16040
|
+
process: child,
|
|
16041
|
+
closed: false
|
|
16042
|
+
};
|
|
16043
|
+
this.child = ownedChild;
|
|
16044
|
+
child.once("close", () => {
|
|
16045
|
+
ownedChild.closed = true;
|
|
16046
|
+
});
|
|
15915
16047
|
child.stdout.resume();
|
|
15916
16048
|
child.stderr.resume();
|
|
15917
16049
|
child.stdin.end();
|
|
15918
16050
|
const failed = (code) => {
|
|
15919
|
-
if (generation !== this.generation || this.child !==
|
|
16051
|
+
if (generation !== this.generation || this.child !== ownedChild) return;
|
|
15920
16052
|
childFailure = code;
|
|
15921
16053
|
startup.abort();
|
|
15922
16054
|
if (this.latest.state === "ready") this.enqueue(async () => {
|
|
@@ -15991,20 +16123,49 @@ var CaddyController = class {
|
|
|
15991
16123
|
this.startupAbort?.abort();
|
|
15992
16124
|
await this.stopResources();
|
|
15993
16125
|
}
|
|
16126
|
+
waitForChildClose(child) {
|
|
16127
|
+
if (child.closed) return Promise.resolve();
|
|
16128
|
+
return new Promise((resolve, reject) => {
|
|
16129
|
+
const finish = (error) => {
|
|
16130
|
+
clearTimeout(timer);
|
|
16131
|
+
child.process.removeListener("close", closed);
|
|
16132
|
+
if (error === void 0) resolve();
|
|
16133
|
+
else reject(error);
|
|
16134
|
+
};
|
|
16135
|
+
const closed = () => {
|
|
16136
|
+
finish();
|
|
16137
|
+
};
|
|
16138
|
+
const timer = setTimeout(() => {
|
|
16139
|
+
finish(/* @__PURE__ */ new Error("caddy_process_close_timeout"));
|
|
16140
|
+
}, 1500);
|
|
16141
|
+
timer.unref();
|
|
16142
|
+
child.process.once("close", closed);
|
|
16143
|
+
});
|
|
16144
|
+
}
|
|
15994
16145
|
async stopResources() {
|
|
15995
16146
|
const child = this.child;
|
|
15996
16147
|
const gateway = this.gatewayValue;
|
|
15997
|
-
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
if (
|
|
16006
|
-
|
|
16007
|
-
|
|
16148
|
+
try {
|
|
16149
|
+
await settleRemoteResources([async () => {
|
|
16150
|
+
if (child !== void 0) {
|
|
16151
|
+
await (this.options.terminateProcess ?? terminateRemoteProcess)(child.process);
|
|
16152
|
+
await this.waitForChildClose(child);
|
|
16153
|
+
if (this.child === child) this.child = void 0;
|
|
16154
|
+
}
|
|
16155
|
+
}, async () => {
|
|
16156
|
+
if (gateway !== void 0) {
|
|
16157
|
+
await gateway.close();
|
|
16158
|
+
if (this.gatewayValue === gateway) this.gatewayValue = void 0;
|
|
16159
|
+
}
|
|
16160
|
+
}], "caddy cleanup failed");
|
|
16161
|
+
} catch (error) {
|
|
16162
|
+
this.publish({
|
|
16163
|
+
enabled: this.enabled,
|
|
16164
|
+
state: "error",
|
|
16165
|
+
errorCode: "caddy_cleanup_failed"
|
|
16166
|
+
});
|
|
16167
|
+
throw error;
|
|
16168
|
+
}
|
|
16008
16169
|
}
|
|
16009
16170
|
};
|
|
16010
16171
|
//#endregion
|
|
@@ -18020,6 +18181,11 @@ async function apply(ctx, config) {
|
|
|
18020
18181
|
adminClosing = true;
|
|
18021
18182
|
unregister();
|
|
18022
18183
|
};
|
|
18184
|
+
const drainRemoteWork = async () => {
|
|
18185
|
+
const draining = remoteProviders.close();
|
|
18186
|
+
caddyController.cancelPendingStart();
|
|
18187
|
+
await settleCleanupSteps([() => caddyComponent.close(), () => draining]);
|
|
18188
|
+
};
|
|
18023
18189
|
const disposeMobileCommand = ctx.commands.register({
|
|
18024
18190
|
name: "mobile",
|
|
18025
18191
|
description: "定制手机界面或添加电脑能力",
|
|
@@ -18086,6 +18252,7 @@ async function apply(ctx, config) {
|
|
|
18086
18252
|
try {
|
|
18087
18253
|
await settleCleanupSteps([
|
|
18088
18254
|
stopAdmin,
|
|
18255
|
+
drainRemoteWork,
|
|
18089
18256
|
() => trustedNetworksWrite,
|
|
18090
18257
|
disposeMobileCommand,
|
|
18091
18258
|
disposeTaskEvents,
|
|
@@ -18105,6 +18272,7 @@ async function apply(ctx, config) {
|
|
|
18105
18272
|
return async () => {
|
|
18106
18273
|
await settleCleanupSteps([
|
|
18107
18274
|
stopAdmin,
|
|
18275
|
+
drainRemoteWork,
|
|
18108
18276
|
() => trustedNetworksWrite,
|
|
18109
18277
|
disposeMobileCommand,
|
|
18110
18278
|
disposeTaskEvents,
|