dsh-mobile 0.4.7 → 0.5.0
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 +10 -0
- package/CONTRIBUTORS.md +5 -2
- package/README.en.md +8 -6
- package/README.md +8 -6
- package/SECURITY.md +1 -1
- package/lib/client.js +263 -12
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +2 -1
- package/lib/index.mjs +137 -39
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-layout.js +51 -4
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +1 -1
package/lib/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { X509Certificate } from "node:crypto";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { ChildProcessWithoutNullStreams } from "node:child_process";
|
|
4
|
+
import { IncomingMessage } from "node:http";
|
|
4
5
|
import { Readable } from "node:stream";
|
|
5
6
|
import { Context, Service } from "@deepseek-ai/cordis";
|
|
6
7
|
import { WebRoute } from "@deepseek-ai/dsh-host-webserver";
|
|
@@ -756,7 +757,7 @@ declare class MobileAccessGateway {
|
|
|
756
757
|
blockedUpgradePathReport(): BlockedUpgradePathEntry[];
|
|
757
758
|
private handleUpgrade;
|
|
758
759
|
/** Loopback-only DSH WebServer route for opening pairing and managing devices. */
|
|
759
|
-
localAdminRoute(prefix?: string): WebRoute;
|
|
760
|
+
localAdminRoute(prefix?: string, authenticateDesktop?: (request: IncomingMessage) => boolean): WebRoute;
|
|
760
761
|
/** Close listeners and abort all accepted work before resolving teardown. */
|
|
761
762
|
close(): Promise<void>;
|
|
762
763
|
private performClose;
|
package/lib/index.mjs
CHANGED
|
@@ -1222,9 +1222,6 @@ var MobileAccessGatewayController = class {
|
|
|
1222
1222
|
return run;
|
|
1223
1223
|
}
|
|
1224
1224
|
};
|
|
1225
|
-
//#endregion
|
|
1226
|
-
//#region src/local-admin-host.ts
|
|
1227
|
-
/** Browser-safe desktop-admin Host checks. Do not import Node APIs here. */
|
|
1228
1225
|
function unwrapBrackets(hostname) {
|
|
1229
1226
|
return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
1230
1227
|
}
|
|
@@ -1339,7 +1336,8 @@ function setSecurityHeaders(response, tls, framing = "gateway") {
|
|
|
1339
1336
|
"worker-src 'self' blob:",
|
|
1340
1337
|
...proxied ? [PROXIED_FRAME_SRC] : []
|
|
1341
1338
|
].join("; "));
|
|
1342
|
-
|
|
1339
|
+
const microphone = proxied ? "microphone=(self)" : "microphone=()";
|
|
1340
|
+
response.setHeader("Permissions-Policy", `camera=(), ${microphone}, geolocation=(), payment=(), usb=()`);
|
|
1343
1341
|
response.setHeader("Referrer-Policy", "no-referrer");
|
|
1344
1342
|
response.setHeader("X-Content-Type-Options", "nosniff");
|
|
1345
1343
|
response.setHeader("X-Frame-Options", proxied ? "SAMEORIGIN" : "DENY");
|
|
@@ -1436,7 +1434,7 @@ function localAuthority(header) {
|
|
|
1436
1434
|
}
|
|
1437
1435
|
}
|
|
1438
1436
|
/** Protect the inner management route from non-loopback and DNS-rebinding callers. */
|
|
1439
|
-
function assertLocalAdminTrust(request, requireBrowserOrigin) {
|
|
1437
|
+
function assertLocalAdminTrust(request, requireBrowserOrigin, authenticateDesktop = () => false) {
|
|
1440
1438
|
if (request.socket.remoteAddress === void 0 || !isLoopbackAddress(request.socket.remoteAddress)) throw new HttpError(403, "forbidden");
|
|
1441
1439
|
const host = localAuthority(request.headers.host);
|
|
1442
1440
|
if (host === void 0 || !isLocalAdminHostname(host.hostname)) throw new HttpError(403, "forbidden");
|
|
@@ -1450,7 +1448,8 @@ function assertLocalAdminTrust(request, requireBrowserOrigin) {
|
|
|
1450
1448
|
if (error instanceof HttpError) throw error;
|
|
1451
1449
|
throw new HttpError(403, "forbidden");
|
|
1452
1450
|
}
|
|
1453
|
-
|
|
1451
|
+
const desktopForward = site === void 0 && request.headers["x-dsh-mobile-desktop-admin"] === "dsh-app://app";
|
|
1452
|
+
if (requireBrowserOrigin && origin === void 0 && !(desktopForward && authenticateDesktop())) throw new HttpError(403, "forbidden");
|
|
1454
1453
|
if (requireBrowserOrigin && site !== void 0 && site !== "same-origin") throw new HttpError(403, "forbidden");
|
|
1455
1454
|
}
|
|
1456
1455
|
//#endregion
|
|
@@ -2647,7 +2646,7 @@ const MOBILE_LAYOUT_DEPENDENCY_PROFILES = Object.freeze([Object.freeze({
|
|
|
2647
2646
|
])
|
|
2648
2647
|
})]);
|
|
2649
2648
|
const MOBILE_CSRF_FETCH_BOOTSTRAP = `(()=>{const nativeFetch=window.fetch.bind(window);window.fetch=(input,init)=>{const source=input instanceof Request?input:undefined;const method=String(init?.method??source?.method??'GET').toUpperCase();if(method==='GET'||method==='HEAD')return nativeFetch(input,init);const raw=typeof input==='string'?input:input instanceof URL?input.href:source?.url;if(raw===undefined||new URL(raw,location.href).origin!==location.origin)return nativeFetch(input,init);const headers=new Headers(init?.headers??source?.headers);if(!headers.has(${JSON.stringify(CSRF_HEADER)})){const prefix=${JSON.stringify(`${CSRF_COOKIE}=`)};const token=document.cookie.split(';').map(value=>value.trim()).find(value=>value.startsWith(prefix))?.slice(prefix.length);if(token!==undefined)headers.set(${JSON.stringify(CSRF_HEADER)},token)}return nativeFetch(input,{...init,headers})};})();`;
|
|
2650
|
-
const MOBILE_AUTHENTICATED_TRANSPORT_BOOTSTRAP = `(()=>{if(window.__DSH_TRANSPORT__!==undefined)throw new Error('DSH Mobile cannot replace an existing transport override');window.__DSH_TRANSPORT__={fetch:(input,init)=>window.fetch(input,init),ownsHost:true}})();`;
|
|
2649
|
+
const MOBILE_AUTHENTICATED_TRANSPORT_BOOTSTRAP = `(()=>{if(window.__DSH_TRANSPORT__!==undefined)throw new Error('DSH Mobile cannot replace an existing transport override');window.__DSH_TRANSPORT__={fetch:(input,init)=>window.fetch(input,init),ownsHost:true},window.__DSH_FILE_UPLOAD__={fetch:(input,init)=>window.fetch(input,init)}})();`;
|
|
2651
2650
|
/** Use this page's gateway health, rather than OS internet validation, for DSH reconnects. */
|
|
2652
2651
|
const MOBILE_GATEWAY_REACHABILITY_BOOTSTRAP = `(()=>{
|
|
2653
2652
|
const view=globalThis;
|
|
@@ -5085,14 +5084,14 @@ var MobileAccessGateway = class {
|
|
|
5085
5084
|
}
|
|
5086
5085
|
}
|
|
5087
5086
|
/** Loopback-only DSH WebServer route for opening pairing and managing devices. */
|
|
5088
|
-
localAdminRoute(prefix = LOCAL_ADMIN_PREFIX) {
|
|
5087
|
+
localAdminRoute(prefix = LOCAL_ADMIN_PREFIX, authenticateDesktop) {
|
|
5089
5088
|
return {
|
|
5090
5089
|
kind: "prefix",
|
|
5091
5090
|
path: prefix,
|
|
5092
5091
|
handler: async (request, response) => {
|
|
5093
5092
|
try {
|
|
5094
5093
|
const target = parseRequestTarget(request.url);
|
|
5095
|
-
assertLocalAdminTrust(request, request.method === "POST");
|
|
5094
|
+
assertLocalAdminTrust(request, request.method === "POST", () => authenticateDesktop?.(request) === true);
|
|
5096
5095
|
if (target.search !== "") throw new HttpError(400, "bad_request");
|
|
5097
5096
|
if (request.method === "GET" && target.decodedPathname === `${prefix}/status`) {
|
|
5098
5097
|
sendJson(response, 200, {
|
|
@@ -9509,6 +9508,10 @@ var TaskEventHub = class {
|
|
|
9509
9508
|
};
|
|
9510
9509
|
//#endregion
|
|
9511
9510
|
//#region src/diagnostics.ts
|
|
9511
|
+
/** Recognize the active remote-web-ui boot hook without evaluating or modifying it. */
|
|
9512
|
+
function hasCompetingRemoteChannelBoot(rows) {
|
|
9513
|
+
return rows.some((row) => row.kind === "script" && row.text?.includes("__DSH_REMOTE_CHANNEL_BOOT__") === true);
|
|
9514
|
+
}
|
|
9512
9515
|
const REMOTE_ERROR_GUIDANCE = Object.freeze({
|
|
9513
9516
|
component_missing: "重新安装完整插件包。",
|
|
9514
9517
|
funnel_permission_required: "继续完成 Tailscale Funnel 授权。",
|
|
@@ -9903,6 +9906,7 @@ async function collectConnectionDiagnostics(snapshot, probes = {}) {
|
|
|
9903
9906
|
const remoteProbe = snapshot.remote.provider !== "origin" && snapshot.remote.running && snapshot.remote.state === "ready" && snapshot.remote.origin !== void 0 ? (probes.remote ?? defaultRemoteProbe)(snapshot.remote.origin) : Promise.resolve({ state: "not-applicable" });
|
|
9904
9907
|
const [firewall, remoteObservation] = await Promise.all([(probes.firewall ?? defaultFirewallProbe())(snapshot.lan.port), remoteProbe]);
|
|
9905
9908
|
checks.push(check("versions", "ok", "versions-current", "版本兼容", `插件 ${DSH_MOBILE_VERSION},DSH ${snapshot.dshVersion},Android App 最低 ${MINIMUM_ANDROID_APP_VERSION}。`));
|
|
9909
|
+
if (snapshot.competingRemoteChannelBoot === true) checks.push(check("remote-plugin-conflict", "warning", "competing-remote-channel", "第三方远程插件", "检测到另一套远程请求通道脚本;手机页面可能进入该插件自己的配对流程。", "在电脑端关闭 dsh-remote-web-ui 的远程访问后刷新,再使用 DSH Mobile 当前二维码配对;不要混用两种配对码。"));
|
|
9906
9910
|
if (snapshot.lan.configured === false) checks.push(check("network", "error", "lan-setup-required", "局域网配置", "尚未完成局域网初始化。", "返回局域网页选择网卡并完成配置。"));
|
|
9907
9911
|
else if (snapshot.lan.networkError !== void 0) checks.push(check("network", "error", "network-unavailable", "局域网网卡", "已保存的网卡当前不可用。", "重新运行 dsh-mobile setup。"));
|
|
9908
9912
|
else if (snapshot.lan.configuredInterface !== void 0) {
|
|
@@ -11851,6 +11855,87 @@ async function regularFile(file, expectedBytes) {
|
|
|
11851
11855
|
throw error;
|
|
11852
11856
|
}
|
|
11853
11857
|
}
|
|
11858
|
+
async function installStep(code, operation) {
|
|
11859
|
+
try {
|
|
11860
|
+
return await operation();
|
|
11861
|
+
} catch (error) {
|
|
11862
|
+
if (error instanceof Error && error.message.startsWith("cpolar_")) throw error;
|
|
11863
|
+
throw new Error(code, { cause: error });
|
|
11864
|
+
}
|
|
11865
|
+
}
|
|
11866
|
+
async function pathExists(path) {
|
|
11867
|
+
try {
|
|
11868
|
+
await lstat(path);
|
|
11869
|
+
return true;
|
|
11870
|
+
} catch (error) {
|
|
11871
|
+
if (error.code === "ENOENT") return false;
|
|
11872
|
+
throw error;
|
|
11873
|
+
}
|
|
11874
|
+
}
|
|
11875
|
+
/** Replace only this version's verified binary, retaining the previous directory until promotion succeeds. */
|
|
11876
|
+
async function publishVerifiedCpolarExecutable(extracted, componentRoot, componentStorage, executableName, operations = {}) {
|
|
11877
|
+
if (dirname(componentStorage) !== componentRoot || !inside(componentRoot, componentStorage) || basename(executableName) !== executableName || executableName === "." || executableName === "..") throw new Error("cpolar component storage escaped its private root");
|
|
11878
|
+
const move = operations.move ?? rename;
|
|
11879
|
+
const remove = operations.remove ?? ((path) => rm(path, {
|
|
11880
|
+
recursive: true,
|
|
11881
|
+
force: true
|
|
11882
|
+
}));
|
|
11883
|
+
const candidate = join(componentRoot, `.install-${randomBytes(12).toString("hex")}`);
|
|
11884
|
+
const backup = join(componentRoot, `.previous-${randomBytes(12).toString("hex")}`);
|
|
11885
|
+
let backupContainsPrevious = false;
|
|
11886
|
+
let replacementInstalled = false;
|
|
11887
|
+
try {
|
|
11888
|
+
await mkdir(candidate, {
|
|
11889
|
+
recursive: true,
|
|
11890
|
+
mode: 448
|
|
11891
|
+
});
|
|
11892
|
+
await copyFile(extracted, join(candidate, executableName));
|
|
11893
|
+
await chmod(join(candidate, executableName), 448);
|
|
11894
|
+
if (await pathExists(componentStorage)) {
|
|
11895
|
+
await move(componentStorage, backup);
|
|
11896
|
+
backupContainsPrevious = true;
|
|
11897
|
+
}
|
|
11898
|
+
try {
|
|
11899
|
+
await move(candidate, componentStorage);
|
|
11900
|
+
replacementInstalled = true;
|
|
11901
|
+
} catch (error) {
|
|
11902
|
+
if (backupContainsPrevious) try {
|
|
11903
|
+
await move(backup, componentStorage);
|
|
11904
|
+
backupContainsPrevious = false;
|
|
11905
|
+
} catch (rollbackError) {
|
|
11906
|
+
process.emitWarning(`cpolar previous component remains at ${backup}; automatic rollback failed`, { code: "DSH_MOBILE_CPOLAR_ROLLBACK_FAILED" });
|
|
11907
|
+
throw new Error("cpolar_storage_failed", { cause: new AggregateError([error, rollbackError]) });
|
|
11908
|
+
}
|
|
11909
|
+
throw error;
|
|
11910
|
+
}
|
|
11911
|
+
} finally {
|
|
11912
|
+
try {
|
|
11913
|
+
await remove(candidate);
|
|
11914
|
+
} catch {
|
|
11915
|
+
process.emitWarning(`cpolar temporary component remains at ${candidate}; cleanup failed`, { code: "DSH_MOBILE_CPOLAR_CLEANUP_FAILED" });
|
|
11916
|
+
}
|
|
11917
|
+
if (replacementInstalled && backupContainsPrevious) try {
|
|
11918
|
+
await remove(backup);
|
|
11919
|
+
} catch {
|
|
11920
|
+
process.emitWarning(`cpolar previous component remains at ${backup}; cleanup failed`, { code: "DSH_MOBILE_CPOLAR_CLEANUP_FAILED" });
|
|
11921
|
+
}
|
|
11922
|
+
}
|
|
11923
|
+
}
|
|
11924
|
+
/** Remove only this install's staging directory without replacing a preceding failure. */
|
|
11925
|
+
async function cleanupCpolarInstallStaging(staging, installFailed, remove = (path) => rm(path, {
|
|
11926
|
+
recursive: true,
|
|
11927
|
+
force: true
|
|
11928
|
+
})) {
|
|
11929
|
+
try {
|
|
11930
|
+
await remove(staging);
|
|
11931
|
+
} catch (error) {
|
|
11932
|
+
if (installFailed) {
|
|
11933
|
+
process.emitWarning(`cpolar staging directory remains at ${staging}; cleanup failed`, { code: "DSH_MOBILE_CPOLAR_CLEANUP_FAILED" });
|
|
11934
|
+
return;
|
|
11935
|
+
}
|
|
11936
|
+
throw new Error("cpolar_storage_failed", { cause: error });
|
|
11937
|
+
}
|
|
11938
|
+
}
|
|
11854
11939
|
async function run(file, args) {
|
|
11855
11940
|
await new Promise((resolveRun, reject) => {
|
|
11856
11941
|
execFile(file, [...args], {
|
|
@@ -12005,11 +12090,14 @@ var CpolarComponentManager = class {
|
|
|
12005
12090
|
return this.enqueue(async () => {
|
|
12006
12091
|
const release = this.release;
|
|
12007
12092
|
if (release === void 0) throw new Error("cpolar_component_unsupported");
|
|
12008
|
-
await
|
|
12009
|
-
|
|
12010
|
-
|
|
12093
|
+
const staging = await installStep("cpolar_storage_failed", async () => {
|
|
12094
|
+
await mkdir(this.stagingRoot, {
|
|
12095
|
+
recursive: true,
|
|
12096
|
+
mode: 448
|
|
12097
|
+
});
|
|
12098
|
+
return mkdtemp(join(this.stagingRoot, "install-"));
|
|
12011
12099
|
});
|
|
12012
|
-
|
|
12100
|
+
let installFailed = false;
|
|
12013
12101
|
try {
|
|
12014
12102
|
const controller = new AbortController();
|
|
12015
12103
|
const timeout = setTimeout(() => {
|
|
@@ -12018,38 +12106,35 @@ var CpolarComponentManager = class {
|
|
|
12018
12106
|
timeout.unref();
|
|
12019
12107
|
let bytes;
|
|
12020
12108
|
try {
|
|
12021
|
-
bytes = await this.fetchArtifact(release.downloadUrl, controller.signal);
|
|
12109
|
+
bytes = await installStep("cpolar_download_failed", () => this.fetchArtifact(release.downloadUrl, controller.signal));
|
|
12110
|
+
} catch (error) {
|
|
12111
|
+
if (controller.signal.aborted) throw new Error("cpolar_download_timeout", { cause: error });
|
|
12112
|
+
throw error;
|
|
12022
12113
|
} finally {
|
|
12023
12114
|
clearTimeout(timeout);
|
|
12024
12115
|
}
|
|
12025
12116
|
if (createHash("sha256").update(bytes).digest("hex") !== release.downloadSha256) throw new Error("cpolar_download_hash_mismatch");
|
|
12026
12117
|
const archive = join(staging, release.archiveKind === "tar.gz" ? "cpolar.tar.gz" : "cpolar.zip");
|
|
12027
|
-
await writeFile(archive, bytes, {
|
|
12118
|
+
await installStep("cpolar_storage_failed", () => writeFile(archive, bytes, {
|
|
12028
12119
|
flag: "wx",
|
|
12029
12120
|
mode: 384
|
|
12030
|
-
});
|
|
12031
|
-
await this.extractArtifact(archive, staging);
|
|
12121
|
+
}));
|
|
12122
|
+
await installStep("cpolar_extract_failed", () => this.extractArtifact(archive, staging));
|
|
12032
12123
|
const extracted = join(staging, release.executableName);
|
|
12033
|
-
if (!await regularFile(extracted, release.executableBytes)
|
|
12034
|
-
|
|
12035
|
-
await mkdir(candidate, {
|
|
12036
|
-
recursive: true,
|
|
12037
|
-
mode: 448
|
|
12038
|
-
});
|
|
12039
|
-
await copyFile(extracted, join(candidate, release.executableName));
|
|
12040
|
-
await chmod(join(candidate, release.executableName), 448);
|
|
12041
|
-
await rm(this.componentStorage, {
|
|
12042
|
-
recursive: true,
|
|
12043
|
-
force: true
|
|
12044
|
-
});
|
|
12045
|
-
await rename(candidate, this.componentStorage);
|
|
12124
|
+
if (!await installStep("cpolar_extract_failed", async () => await regularFile(extracted, release.executableBytes) && await sha256(extracted) === release.executableSha256)) throw new Error("cpolar_executable_hash_mismatch");
|
|
12125
|
+
await installStep("cpolar_storage_failed", () => publishVerifiedCpolarExecutable(extracted, this.componentRoot, this.componentStorage, release.executableName));
|
|
12046
12126
|
this.installed = true;
|
|
12047
12127
|
this.errorCode = void 0;
|
|
12128
|
+
} catch (error) {
|
|
12129
|
+
installFailed = true;
|
|
12130
|
+
try {
|
|
12131
|
+
this.installed = await regularFile(this.executable, release.executableBytes) && await sha256(this.executable) === release.executableSha256;
|
|
12132
|
+
} catch (_inspectionError) {
|
|
12133
|
+
this.installed = false;
|
|
12134
|
+
}
|
|
12135
|
+
throw error;
|
|
12048
12136
|
} finally {
|
|
12049
|
-
await
|
|
12050
|
-
recursive: true,
|
|
12051
|
-
force: true
|
|
12052
|
-
});
|
|
12137
|
+
await cleanupCpolarInstallStaging(staging, installFailed);
|
|
12053
12138
|
}
|
|
12054
12139
|
});
|
|
12055
12140
|
}
|
|
@@ -13223,6 +13308,7 @@ async function apply(ctx, config) {
|
|
|
13223
13308
|
} : void 0;
|
|
13224
13309
|
return collectConnectionDiagnostics({
|
|
13225
13310
|
dshVersion,
|
|
13311
|
+
competingRemoteChannelBoot: hasCompetingRemoteChannelBoot(ctx.webServer.collectIndexInjections?.() ?? []),
|
|
13226
13312
|
lan: {
|
|
13227
13313
|
configured: loaded.kind !== "unconfigured" || preparedLanSetup !== void 0,
|
|
13228
13314
|
running: lanController.isRunning(),
|
|
@@ -13246,13 +13332,17 @@ async function apply(ctx, config) {
|
|
|
13246
13332
|
}
|
|
13247
13333
|
}, pinnedFrpProbe === void 0 ? {} : { remote: pinnedFrpProbe });
|
|
13248
13334
|
};
|
|
13335
|
+
const authenticateDesktopAdmin = (request) => {
|
|
13336
|
+
const connection = ctx.connection;
|
|
13337
|
+
return typeof connection?.requestRejection === "function" && connection.requestRejection(request) === void 0;
|
|
13338
|
+
};
|
|
13249
13339
|
const adminRoute = {
|
|
13250
13340
|
kind: "prefix",
|
|
13251
13341
|
path: LOCAL_ADMIN_PREFIX,
|
|
13252
13342
|
handler: async (request, response) => {
|
|
13253
13343
|
try {
|
|
13254
13344
|
const target = parseRequestTarget(request.url);
|
|
13255
|
-
assertLocalAdminTrust(request, request.method === "POST");
|
|
13345
|
+
assertLocalAdminTrust(request, request.method === "POST", () => authenticateDesktopAdmin(request));
|
|
13256
13346
|
if (target.search !== "") throw new HttpError(400, "bad_request");
|
|
13257
13347
|
const lanControl = target.decodedPathname === `/api/mobile-access/control` || target.decodedPathname === `/api/mobile-access/lan/control`;
|
|
13258
13348
|
if (request.method === "GET" && lanControl) {
|
|
@@ -13341,7 +13431,15 @@ async function apply(ctx, config) {
|
|
|
13341
13431
|
}
|
|
13342
13432
|
if (request.method === "POST" && target.decodedPathname === `/api/mobile-access/remote/cpolar/component/install`) {
|
|
13343
13433
|
if ((await readJsonObject(request, 4096)).confirm !== true) throw new HttpError(400, "bad_request");
|
|
13344
|
-
|
|
13434
|
+
logger.info("cpolar component install started");
|
|
13435
|
+
try {
|
|
13436
|
+
await remoteProviders.mutate(async () => cpolarComponent.install());
|
|
13437
|
+
logger.info("cpolar component install completed");
|
|
13438
|
+
} catch (error) {
|
|
13439
|
+
const cause = error instanceof Error && error.cause instanceof Error ? error.cause : void 0;
|
|
13440
|
+
logger.error("cpolar component install failed: %s%s", error instanceof Error ? error.stack ?? error.message : String(error), cause === void 0 ? "" : `; cause: ${cause.message}`);
|
|
13441
|
+
throw error;
|
|
13442
|
+
}
|
|
13345
13443
|
sendJson(response, 200, remotePayload(), false);
|
|
13346
13444
|
return;
|
|
13347
13445
|
}
|
|
@@ -13584,18 +13682,18 @@ async function apply(ctx, config) {
|
|
|
13584
13682
|
if (target.decodedPathname.startsWith(`/api/mobile-access/remote/`)) {
|
|
13585
13683
|
const active = remoteController().gateway();
|
|
13586
13684
|
if (active === void 0) throw new HttpError(409, "gateway_stopped");
|
|
13587
|
-
await active.localAdminRoute(`${LOCAL_ADMIN_PREFIX}/remote
|
|
13685
|
+
await active.localAdminRoute(`${LOCAL_ADMIN_PREFIX}/remote`, authenticateDesktopAdmin).handler(request, response);
|
|
13588
13686
|
return;
|
|
13589
13687
|
}
|
|
13590
13688
|
if (target.decodedPathname.startsWith(`/api/mobile-access/lan/`)) {
|
|
13591
13689
|
const active = lanGateway;
|
|
13592
13690
|
if (active === void 0) throw new HttpError(409, "gateway_stopped");
|
|
13593
|
-
await active.localAdminRoute(`${LOCAL_ADMIN_PREFIX}/lan
|
|
13691
|
+
await active.localAdminRoute(`${LOCAL_ADMIN_PREFIX}/lan`, authenticateDesktopAdmin).handler(request, response);
|
|
13594
13692
|
return;
|
|
13595
13693
|
}
|
|
13596
13694
|
const active = lanGateway;
|
|
13597
13695
|
if (active === void 0) throw new HttpError(409, "gateway_stopped");
|
|
13598
|
-
await active.localAdminRoute().handler(request, response);
|
|
13696
|
+
await active.localAdminRoute(LOCAL_ADMIN_PREFIX, authenticateDesktopAdmin).handler(request, response);
|
|
13599
13697
|
} catch (error) {
|
|
13600
13698
|
const mapped = mapAdminError(error);
|
|
13601
13699
|
if (response.headersSent) response.destroy();
|