dsh-zen-remote 2.2.8 → 2.3.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 +2 -2
- package/README.zh-CN.md +2 -2
- package/docs/interface.md +1 -1
- package/docs/remote-access.md +20 -0
- package/lan-gate.mjs +11 -1
- package/lib/adapters/dsh-better-sidebar/index.js +6 -2
- package/lib/adapters/dsh-better-sidebar/index.js.map +1 -1
- package/lib/adapters/dsh-better-sidebar/methods.js +3 -0
- package/lib/adapters/dsh-better-sidebar/methods.js.map +1 -1
- package/lib/admin-routes.js +33 -2
- package/lib/admin-routes.js.map +1 -1
- package/lib/client-routes.js +52 -0
- package/lib/client-routes.js.map +1 -1
- package/lib/client.js +3390 -10
- package/lib/config.js +70 -0
- package/lib/config.js.map +1 -1
- package/lib/index.js +232 -2
- package/lib/index.js.map +1 -1
- package/lib/intercept.js +43 -3
- package/lib/intercept.js.map +1 -1
- package/lib/lan-gate-server.cjs +29 -3
- package/lib/merge-streams.js +42 -12
- package/lib/merge-streams.js.map +1 -1
- package/lib/relay-client.js +71 -0
- package/lib/relay-client.js.map +1 -1
- package/lib/types/adapters/dsh-better-sidebar/index.d.ts +1 -1
- package/lib/types/adapters/dsh-better-sidebar/index.d.ts.map +1 -1
- package/lib/types/adapters/dsh-better-sidebar/methods.d.ts.map +1 -1
- package/lib/types/admin-routes.d.ts +13 -0
- package/lib/types/admin-routes.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +204 -1
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/wizard/GatewayWizard.d.ts +29 -0
- package/lib/types/client/wizard/GatewayWizard.d.ts.map +1 -0
- package/lib/types/client/wizard/WizardCard.d.ts +30 -0
- package/lib/types/client/wizard/WizardCard.d.ts.map +1 -0
- package/lib/types/client/wizard/cards/MigrateCard.d.ts +15 -0
- package/lib/types/client/wizard/cards/MigrateCard.d.ts.map +1 -0
- package/lib/types/client/wizard/cards/PortForwardCard.d.ts +15 -0
- package/lib/types/client/wizard/cards/PortForwardCard.d.ts.map +1 -0
- package/lib/types/client/wizard/cards/ProxyCard.d.ts +19 -0
- package/lib/types/client/wizard/cards/ProxyCard.d.ts.map +1 -0
- package/lib/types/client/wizard/cards/TokenCard.d.ts +20 -0
- package/lib/types/client/wizard/cards/TokenCard.d.ts.map +1 -0
- package/lib/types/client/wizard/register-wizard.d.ts +51 -0
- package/lib/types/client/wizard/register-wizard.d.ts.map +1 -0
- package/lib/types/client/wizard/wizard-css.d.ts +10 -0
- package/lib/types/client/wizard/wizard-css.d.ts.map +1 -0
- package/lib/types/client-data/qr.d.ts +20 -0
- package/lib/types/client-data/qr.d.ts.map +1 -0
- package/lib/types/client-data/remote-status.d.ts +39 -2
- package/lib/types/client-data/remote-status.d.ts.map +1 -1
- package/lib/types/client-data/settings-form.d.ts +7 -2
- package/lib/types/client-data/settings-form.d.ts.map +1 -1
- package/lib/types/client-data/wizard.d.ts +247 -0
- package/lib/types/client-data/wizard.d.ts.map +1 -0
- package/lib/types/client-routes.d.ts +19 -0
- package/lib/types/client-routes.d.ts.map +1 -1
- package/lib/types/config.d.ts +27 -0
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/intercept.d.ts.map +1 -1
- package/lib/types/merge-streams.d.ts.map +1 -1
- package/lib/types/relay-client.d.ts +10 -0
- package/lib/types/relay-client.d.ts.map +1 -1
- package/lib/types/wizard/binary-io.d.ts +111 -0
- package/lib/types/wizard/binary-io.d.ts.map +1 -0
- package/lib/types/wizard/binary.d.ts +118 -0
- package/lib/types/wizard/binary.d.ts.map +1 -0
- package/lib/types/wizard/caddy-process.d.ts +101 -0
- package/lib/types/wizard/caddy-process.d.ts.map +1 -0
- package/lib/types/wizard/caddy.d.ts +53 -0
- package/lib/types/wizard/caddy.d.ts.map +1 -0
- package/lib/types/wizard/cloudflare.d.ts +112 -0
- package/lib/types/wizard/cloudflare.d.ts.map +1 -0
- package/lib/types/wizard/cloudflared.d.ts +71 -0
- package/lib/types/wizard/cloudflared.d.ts.map +1 -0
- package/lib/types/wizard/probe.d.ts +66 -0
- package/lib/types/wizard/probe.d.ts.map +1 -0
- package/lib/types/wizard/proxy-snippets.d.ts +60 -0
- package/lib/types/wizard/proxy-snippets.d.ts.map +1 -0
- package/lib/types/wizard/routes.d.ts +152 -0
- package/lib/types/wizard/routes.d.ts.map +1 -0
- package/lib/types/wizard/source-detect.d.ts +53 -0
- package/lib/types/wizard/source-detect.d.ts.map +1 -0
- package/lib/types/wizard/status.d.ts +115 -0
- package/lib/types/wizard/status.d.ts.map +1 -0
- package/lib/types/wizard/token-store.d.ts +27 -0
- package/lib/types/wizard/token-store.d.ts.map +1 -0
- package/lib/types/wizard/tunnel-orchestrator.d.ts +90 -0
- package/lib/types/wizard/tunnel-orchestrator.d.ts.map +1 -0
- package/lib/types/wizard/verify.d.ts +97 -0
- package/lib/types/wizard/verify.d.ts.map +1 -0
- package/lib/wizard/binary-io.js +386 -0
- package/lib/wizard/binary-io.js.map +1 -0
- package/lib/wizard/binary.js +138 -0
- package/lib/wizard/binary.js.map +1 -0
- package/lib/wizard/caddy-process.js +162 -0
- package/lib/wizard/caddy-process.js.map +1 -0
- package/lib/wizard/caddy.js +80 -0
- package/lib/wizard/caddy.js.map +1 -0
- package/lib/wizard/cloudflare.js +334 -0
- package/lib/wizard/cloudflare.js.map +1 -0
- package/lib/wizard/cloudflared.js +158 -0
- package/lib/wizard/cloudflared.js.map +1 -0
- package/lib/wizard/probe.js +118 -0
- package/lib/wizard/probe.js.map +1 -0
- package/lib/wizard/proxy-snippets.js +112 -0
- package/lib/wizard/proxy-snippets.js.map +1 -0
- package/lib/wizard/routes.js +462 -0
- package/lib/wizard/routes.js.map +1 -0
- package/lib/wizard/source-detect.js +77 -0
- package/lib/wizard/source-detect.js.map +1 -0
- package/lib/wizard/status.js +94 -0
- package/lib/wizard/status.js.map +1 -0
- package/lib/wizard/token-store.js +71 -0
- package/lib/wizard/token-store.js.map +1 -0
- package/lib/wizard/tunnel-orchestrator.js +155 -0
- package/lib/wizard/tunnel-orchestrator.js.map +1 -0
- package/lib/wizard/verify.js +171 -0
- package/lib/wizard/verify.js.map +1 -0
- package/package.json +37 -37
- package/scripts/check-settings-form.mjs +5 -4
- package/scripts/check-wizard-ui.mjs +690 -0
- package/src/adapters/dsh-better-sidebar/index.ts +6 -2
- package/src/adapters/dsh-better-sidebar/methods.ts +3 -0
- package/src/admin-routes.ts +44 -2
- package/src/client/index.tsx +9 -0
- package/src/client/locales.ts +410 -2
- package/src/client/wizard/GatewayWizard.tsx +2220 -0
- package/src/client/wizard/WizardCard.tsx +81 -0
- package/src/client/wizard/cards/MigrateCard.tsx +42 -0
- package/src/client/wizard/cards/PortForwardCard.tsx +37 -0
- package/src/client/wizard/cards/ProxyCard.tsx +56 -0
- package/src/client/wizard/cards/TokenCard.tsx +81 -0
- package/src/client/wizard/register-wizard.ts +99 -0
- package/src/client/wizard/wizard-css.ts +948 -0
- package/src/client-data/qr.ts +284 -0
- package/src/client-data/remote-status.ts +128 -2
- package/src/client-data/settings-form.ts +12 -3
- package/src/client-data/wizard.ts +432 -0
- package/src/client-routes.ts +53 -0
- package/src/config.ts +87 -0
- package/src/index.ts +227 -2
- package/src/intercept.ts +43 -3
- package/src/merge-streams.ts +41 -12
- package/src/relay-client.ts +76 -0
- package/src/wizard/binary-io.ts +449 -0
- package/src/wizard/binary.ts +177 -0
- package/src/wizard/caddy-process.ts +228 -0
- package/src/wizard/caddy.ts +97 -0
- package/src/wizard/cloudflare.ts +413 -0
- package/src/wizard/cloudflared.ts +196 -0
- package/src/wizard/probe.ts +161 -0
- package/src/wizard/proxy-snippets.ts +128 -0
- package/src/wizard/routes.ts +537 -0
- package/src/wizard/source-detect.ts +87 -0
- package/src/wizard/status.ts +180 -0
- package/src/wizard/token-store.ts +73 -0
- package/src/wizard/tunnel-orchestrator.ts +197 -0
- package/src/wizard/verify.ts +236 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cloudflared process lifecycle (GW-7, 路 C's last mile). The tunnel's
|
|
3
|
+
* connector runs in the foreground (`tunnel run`) as a child of the DSH
|
|
4
|
+
* host, managed through the same subprocess service as the gateway child
|
|
5
|
+
* and the managed Caddy.
|
|
6
|
+
*
|
|
7
|
+
* The connector secret NEVER touches argv (visible to every process on
|
|
8
|
+
* the machine via ps) and is never an environment variable either — it is
|
|
9
|
+
* written to `<DSH_HOME>/zen-remote-ingress/tunnel-token`, mode 0600,
|
|
10
|
+
* through a temp-file-then-rename so a crash mid-write can never leave a
|
|
11
|
+
* half token at the real path, and handed to cloudflared with
|
|
12
|
+
* `--token-file`.
|
|
13
|
+
*
|
|
14
|
+
* Idempotency and cleanup mirror caddy-process.ts: a live child refuses a
|
|
15
|
+
* second start (stop first to switch tunnels), stop is idempotent, and an
|
|
16
|
+
* exited child clears its own slot. The host wiring hands stop to
|
|
17
|
+
* ctx.effect so the row's disposal kills cloudflared too.
|
|
18
|
+
*/
|
|
19
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { ensureBinary, BinaryDownloadError } from './binary-io.js';
|
|
22
|
+
import { ingressDir } from './binary.js';
|
|
23
|
+
/** Where the connector secret lives (design.md: 不进 Config 的字段). */
|
|
24
|
+
export function tunnelTokenPath(dshHome) {
|
|
25
|
+
return join(ingressDir(dshHome), 'tunnel-token');
|
|
26
|
+
}
|
|
27
|
+
/** Whether a non-empty tunnel-token file exists (the projection's boolean
|
|
28
|
+
* only — GW-fix-4/M1: the manager's EXISTENCE says nothing about whether a
|
|
29
|
+
* token has been stored). */
|
|
30
|
+
export function tunnelTokenConfigured(dshHome) {
|
|
31
|
+
try {
|
|
32
|
+
return statSync(tunnelTokenPath(dshHome)).isFile() && readFileSync(tunnelTokenPath(dshHome), 'utf8').trim() !== '';
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Persist the tunnel connector secret: 0600, atomic (write the `.part`
|
|
40
|
+
* sibling, then rename over the real path).
|
|
41
|
+
*/
|
|
42
|
+
export function writeTunnelTokenFile(dshHome, token) {
|
|
43
|
+
const finalPath = tunnelTokenPath(dshHome);
|
|
44
|
+
const partPath = `${finalPath}.part`;
|
|
45
|
+
try {
|
|
46
|
+
mkdirSync(ingressDir(dshHome), { recursive: true });
|
|
47
|
+
writeFileSync(partPath, token, { mode: 0o600 });
|
|
48
|
+
try {
|
|
49
|
+
renameSync(partPath, finalPath);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
writeFileSync(finalPath, token, { mode: 0o600 });
|
|
53
|
+
rmSync(partPath, { force: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
rmSync(partPath, { force: true });
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
return finalPath;
|
|
61
|
+
}
|
|
62
|
+
export function createCloudflaredManager(options) {
|
|
63
|
+
const { subprocess, dshHome } = options;
|
|
64
|
+
const maxLogBytes = options.maxLogBytes ?? 64 * 1024;
|
|
65
|
+
let handle = null;
|
|
66
|
+
let startedOnce = false;
|
|
67
|
+
// GW-fix-2/L3: whitelist env — the host process carries the relay secret
|
|
68
|
+
// and DSH's own credentials, and cloudflared needs none of them (its
|
|
69
|
+
// connector secret comes through the 0600 token file). Include Windows
|
|
70
|
+
// system/profile variables so Go's TLS root-cert store and winsock
|
|
71
|
+
// initialize cleanly on Windows.
|
|
72
|
+
const childEnv = () => {
|
|
73
|
+
const env = {};
|
|
74
|
+
for (const key of [
|
|
75
|
+
'PATH', 'HOME', 'TMPDIR', 'LANG', 'TZ',
|
|
76
|
+
'SystemRoot', 'SYSTEMROOT', 'windir', 'WINDIR',
|
|
77
|
+
'USERPROFILE', 'APPDATA', 'LOCALAPPDATA', 'PROGRAMDATA', 'TEMP', 'TMP',
|
|
78
|
+
]) {
|
|
79
|
+
if (process.env[key] !== undefined)
|
|
80
|
+
env[key] = process.env[key];
|
|
81
|
+
}
|
|
82
|
+
return env;
|
|
83
|
+
};
|
|
84
|
+
const spawnChild = (argv) => subprocess.spawn({
|
|
85
|
+
argv,
|
|
86
|
+
cwd: dshHome,
|
|
87
|
+
// GW-fix-2/L3: whitelist env — the token rides the 0600 file, so the
|
|
88
|
+
// child needs nothing from the host's environment beyond the basics.
|
|
89
|
+
env: childEnv(),
|
|
90
|
+
stdio: {
|
|
91
|
+
stdin: 'ignore',
|
|
92
|
+
stdout: { maxBytes: maxLogBytes },
|
|
93
|
+
stderr: { maxBytes: maxLogBytes },
|
|
94
|
+
},
|
|
95
|
+
graceMs: 3000,
|
|
96
|
+
});
|
|
97
|
+
const clearSlot = (child) => {
|
|
98
|
+
if (handle === child)
|
|
99
|
+
handle = null;
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
async start(input) {
|
|
103
|
+
if (handle !== null)
|
|
104
|
+
return { ok: false, error: 'already-running' };
|
|
105
|
+
const tunnelToken = typeof input.tunnelToken === 'string' && input.tunnelToken.trim() !== '' ? input.tunnelToken : undefined;
|
|
106
|
+
if (tunnelToken === undefined)
|
|
107
|
+
return { ok: false, error: 'missing-token' };
|
|
108
|
+
let binaryPath;
|
|
109
|
+
try {
|
|
110
|
+
binaryPath = (await ensureBinary(dshHome, 'cloudflared', {
|
|
111
|
+
fetchImpl: options.fetchImpl,
|
|
112
|
+
platform: options.platform,
|
|
113
|
+
onProgress: options.onProgress,
|
|
114
|
+
})).path;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (error instanceof BinaryDownloadError) {
|
|
118
|
+
return { ok: false, error: 'download-failed', detail: error.message, installUrl: error.installUrl };
|
|
119
|
+
}
|
|
120
|
+
return { ok: false, error: 'download-failed', detail: String(error?.message ?? error) };
|
|
121
|
+
}
|
|
122
|
+
let tokenFile;
|
|
123
|
+
try {
|
|
124
|
+
tokenFile = writeTunnelTokenFile(dshHome, tunnelToken);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
return { ok: false, error: 'spawn-failed', detail: String(error?.message ?? error) };
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const child = spawnChild([binaryPath, 'tunnel', 'run', '--token-file', tokenFile]);
|
|
131
|
+
handle = child;
|
|
132
|
+
startedOnce = true;
|
|
133
|
+
void child.done.then(() => clearSlot(child)).catch(() => clearSlot(child));
|
|
134
|
+
return { ok: true, binaryPath, tokenFile, alreadyRunning: false };
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
handle = null;
|
|
138
|
+
return { ok: false, error: 'spawn-failed', detail: String(error?.message ?? error) };
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
stop() {
|
|
142
|
+
if (handle === null)
|
|
143
|
+
return;
|
|
144
|
+
const child = handle;
|
|
145
|
+
handle = null;
|
|
146
|
+
try {
|
|
147
|
+
void child.terminate();
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// A dead child's terminate is a no-op or a throw — either way done.
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
isRunning: () => handle !== null,
|
|
154
|
+
/** Whether any start has succeeded in this manager's lifetime. */
|
|
155
|
+
hasStarted: () => startedOnce,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=cloudflared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflared.js","sourceRoot":"","sources":["../../src/wizard/cloudflared.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hostPath probe (GW-8): the server machine walks its OWN public URL and
|
|
3
|
+
* classifies what answers, so the wizard can say something sharper than
|
|
4
|
+
* "it didn't work" — the pairing page showing up means TLS + proxying to
|
|
5
|
+
* the gateway both work; the admin surface answering 200 means the proxy
|
|
6
|
+
* is DROPPING the forwarded headers the gateway trusts; a redirect smells
|
|
7
|
+
* like Cloudflare Access or a proxy rewriting to a login page.
|
|
8
|
+
*
|
|
9
|
+
* Split exactly as the ticket asks: the two `classify*Response` functions
|
|
10
|
+
* are pure (the check scripts can drive them), `probeHostPath` owns the
|
|
11
|
+
* fetch I/O — `redirect: 'manual'` so a proxy's 3xx is SEEN, never
|
|
12
|
+
* followed — with the URL admission reusing client-pairing's
|
|
13
|
+
* `normalizeServerUrl` (https anywhere; plain http only for the explicit
|
|
14
|
+
* private-network allowances) and a 10 s ceiling per request.
|
|
15
|
+
*/
|
|
16
|
+
import { normalizeServerUrl } from '../client-pairing.js';
|
|
17
|
+
/**
|
|
18
|
+
* `GET /`: the ONE right answer is the gateway's pairing page — 401,
|
|
19
|
+
* text/html, carrying the claim-endpoint marker the page's own script
|
|
20
|
+
* posts to. A 200 means the URL belongs to something else entirely.
|
|
21
|
+
*/
|
|
22
|
+
export function classifyRootResponse(status, contentType, body) {
|
|
23
|
+
if (status === 401) {
|
|
24
|
+
if (contentType.includes('text/html') && body.includes('/lan-gate/pair/claim'))
|
|
25
|
+
return 'gateway-pairing-page';
|
|
26
|
+
return 'unexpected-401';
|
|
27
|
+
}
|
|
28
|
+
if (status >= 200 && status < 300)
|
|
29
|
+
return 'unexpected-200';
|
|
30
|
+
if (status >= 300 && status < 400)
|
|
31
|
+
return 'redirect';
|
|
32
|
+
return 'unexpected';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* `GET /lan-gate/admin`: right is the gateway's own 403 JSON — a public
|
|
36
|
+
* visitor must not get in. 200 + HTML is the DANGEROUS shape: something
|
|
37
|
+
* answered with a page, meaning the proxy forwarded without the
|
|
38
|
+
* forwarded headers and the gateway's local-only wall never saw a
|
|
39
|
+
* proxied request (the admin page leaked). 403 + HTML smells like
|
|
40
|
+
* Cloudflare Access interposing its own login.
|
|
41
|
+
*/
|
|
42
|
+
export function classifyAdminResponse(status, contentType, body) {
|
|
43
|
+
const isHtml = contentType.includes('text/html');
|
|
44
|
+
if (status === 403) {
|
|
45
|
+
if (isHtml)
|
|
46
|
+
return 'proxy-403';
|
|
47
|
+
if (contentType.includes('application/json')) {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(body);
|
|
50
|
+
if (parsed !== null && typeof parsed === 'object' && parsed.ok === false) {
|
|
51
|
+
return 'gateway-admin-403';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// JSON-shaped header with a non-JSON body: not our gateway's answer.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return 'unexpected';
|
|
59
|
+
}
|
|
60
|
+
if (status >= 200 && status < 300 && isHtml)
|
|
61
|
+
return 'admin-exposed';
|
|
62
|
+
return 'unexpected';
|
|
63
|
+
}
|
|
64
|
+
/** One manual-redirect GET, classified. Sends a browser-shaped `Accept:
|
|
65
|
+
* text/html` (never `application/json`, which tells `lan-gate-server.cjs`
|
|
66
|
+
* the caller is an XHR/API client and makes `GET /` answer 401 JSON
|
|
67
|
+
* `{"ok":false,"reason":"unpaired"}` instead of the HTML pairing page;
|
|
68
|
+
* `/lan-gate/admin` answers 403 JSON regardless of `Accept`). */
|
|
69
|
+
async function probeOnce(fetchImpl, url, timeoutMs) {
|
|
70
|
+
const response = await fetchImpl(url, {
|
|
71
|
+
redirect: 'manual',
|
|
72
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
73
|
+
headers: { Accept: 'text/html' },
|
|
74
|
+
});
|
|
75
|
+
const body = await response.text();
|
|
76
|
+
return { status: response.status, contentType: String(response.headers.get('content-type') ?? ''), body };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Probe the machine's public URL from here: both the root path and the
|
|
80
|
+
* admin surface, never following a redirect. The two requests run
|
|
81
|
+
* concurrently; the answer carries both classifications and the elapsed
|
|
82
|
+
* time. Every failure mode is a VALUE (`kind: 'unreachable'`), never a
|
|
83
|
+
* throw — the wizard routes render whichever verdict arrived.
|
|
84
|
+
*/
|
|
85
|
+
export async function probeHostPath(publicUrl, options = {}) {
|
|
86
|
+
const started = (options.now ?? Date.now)();
|
|
87
|
+
const elapsed = () => (options.now ?? Date.now)() - started;
|
|
88
|
+
// Same admission the client pairing uses: https anywhere, plain http
|
|
89
|
+
// only into the explicit private-network allowances.
|
|
90
|
+
const normalized = normalizeServerUrl(typeof publicUrl === 'string' ? publicUrl : '');
|
|
91
|
+
if (!normalized.ok) {
|
|
92
|
+
return { kind: 'unreachable', reason: normalized.reason === 'insecure-http' ? 'insecure-http' : 'invalid-url', ms: 0 };
|
|
93
|
+
}
|
|
94
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
95
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
96
|
+
try {
|
|
97
|
+
const [root, admin] = await Promise.all([
|
|
98
|
+
probeOnce(fetchImpl, `${normalized.url}/`, timeoutMs),
|
|
99
|
+
probeOnce(fetchImpl, `${normalized.url}/lan-gate/admin`, timeoutMs),
|
|
100
|
+
]);
|
|
101
|
+
return {
|
|
102
|
+
kind: 'reached',
|
|
103
|
+
root: classifyRootResponse(root.status, root.contentType, root.body),
|
|
104
|
+
admin: classifyAdminResponse(admin.status, admin.contentType, admin.body),
|
|
105
|
+
ms: elapsed(),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const name = error?.name;
|
|
110
|
+
return {
|
|
111
|
+
kind: 'unreachable',
|
|
112
|
+
reason: name === 'TimeoutError' || name === 'AbortError' ? 'timeout' : 'network',
|
|
113
|
+
detail: String(error?.message ?? error),
|
|
114
|
+
ms: elapsed(),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/wizard/probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reverse-proxy configuration snippets (GW-5, 路 B) — for the user who
|
|
3
|
+
* ALREADY runs their own proxy, the wizard hands out copy-paste material
|
|
4
|
+
* instead of managing a process. Everything here is pure text generation:
|
|
5
|
+
* every interpolated value passes a charset gate (the domain through
|
|
6
|
+
* config.ts's `hostOf`, the upstream host likewise, the port in [1,
|
|
7
|
+
* 65535]), so a hostile form value cannot smuggle a directive into
|
|
8
|
+
* anyone's nginx.
|
|
9
|
+
*
|
|
10
|
+
* The upstream port is the gateway's ACTUAL port — the caller reads it
|
|
11
|
+
* from the running gateway (it may have retried past the configured one),
|
|
12
|
+
* never from config.
|
|
13
|
+
*/
|
|
14
|
+
import { hostOf } from '../config.js';
|
|
15
|
+
/** Integers in [1, 65535] only. */
|
|
16
|
+
function portOf(value) {
|
|
17
|
+
return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 65535 ? value : undefined;
|
|
18
|
+
}
|
|
19
|
+
function validatedParts(input) {
|
|
20
|
+
const domain = hostOf(input.domain);
|
|
21
|
+
if (domain === undefined)
|
|
22
|
+
return { error: { ok: false, error: 'bad-domain' } };
|
|
23
|
+
const upstream = hostOf(input.upstreamHost);
|
|
24
|
+
if (upstream === undefined)
|
|
25
|
+
return { error: { ok: false, error: 'bad-upstream-host' } };
|
|
26
|
+
const port = portOf(input.upstreamPort);
|
|
27
|
+
if (port === undefined)
|
|
28
|
+
return { error: { ok: false, error: 'bad-upstream-port' } };
|
|
29
|
+
return { domain, upstream, port };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* nginx: a complete server block. The WebSocket upgrade map sits outside
|
|
33
|
+
* the server (http-level context) — the snippet says so in a comment, the
|
|
34
|
+
* user pastes it into the http/server scope their setup offers. Long
|
|
35
|
+
* idle tunnels need the 3600s read/send timeouts, uploads the 128m
|
|
36
|
+
* client body cap, and streaming the buffering switch — all three are
|
|
37
|
+
* the shapes DSH's relay traffic actually needs.
|
|
38
|
+
*/
|
|
39
|
+
export function nginxSnippet(input) {
|
|
40
|
+
const parts = validatedParts(input);
|
|
41
|
+
if ('error' in parts)
|
|
42
|
+
return parts.error;
|
|
43
|
+
const { domain, upstream, port } = parts;
|
|
44
|
+
const text = [
|
|
45
|
+
'# 这段放在 http {} 层(若已有同名 map 则跳过)',
|
|
46
|
+
'map $http_upgrade $connection_upgrade {',
|
|
47
|
+
' default upgrade;',
|
|
48
|
+
" '' close;",
|
|
49
|
+
'}',
|
|
50
|
+
'',
|
|
51
|
+
'server {',
|
|
52
|
+
' listen 443 ssl;',
|
|
53
|
+
` server_name ${domain};`,
|
|
54
|
+
'',
|
|
55
|
+
' location / {',
|
|
56
|
+
` proxy_pass http://${upstream}:${port};`,
|
|
57
|
+
' proxy_http_version 1.1;',
|
|
58
|
+
' proxy_set_header Upgrade $http_upgrade;',
|
|
59
|
+
' proxy_set_header Connection $connection_upgrade;',
|
|
60
|
+
' proxy_set_header Host $host;',
|
|
61
|
+
' proxy_set_header X-Real-IP $remote_addr;',
|
|
62
|
+
' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;',
|
|
63
|
+
' proxy_set_header X-Forwarded-Proto $scheme;',
|
|
64
|
+
' proxy_buffering off;',
|
|
65
|
+
' client_max_body_size 128m;',
|
|
66
|
+
' proxy_read_timeout 3600s;',
|
|
67
|
+
' proxy_send_timeout 3600s;',
|
|
68
|
+
' }',
|
|
69
|
+
'}',
|
|
70
|
+
'',
|
|
71
|
+
].join('\n');
|
|
72
|
+
return { ok: true, text };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Caddy (user-operated, not the wizard-managed one): the site block is
|
|
76
|
+
* one `reverse_proxy` line — Caddy routes the rest itself.
|
|
77
|
+
*/
|
|
78
|
+
export function caddySnippet(input) {
|
|
79
|
+
const parts = validatedParts(input);
|
|
80
|
+
if ('error' in parts)
|
|
81
|
+
return parts.error;
|
|
82
|
+
const { domain, upstream, port } = parts;
|
|
83
|
+
const text = [
|
|
84
|
+
`${domain} {`,
|
|
85
|
+
` reverse_proxy ${upstream}:${port}`,
|
|
86
|
+
'}',
|
|
87
|
+
'',
|
|
88
|
+
].join('\n');
|
|
89
|
+
return { ok: true, text };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Lucky (Web 服务反代) three steps: listen 443, proxy to the upstream,
|
|
93
|
+
* flip the catch-all switch. Text only — Lucky has no config file format
|
|
94
|
+
* to generate.
|
|
95
|
+
*/
|
|
96
|
+
export function luckySteps(input) {
|
|
97
|
+
const upstream = hostOf(input.upstreamHost);
|
|
98
|
+
if (upstream === undefined)
|
|
99
|
+
return { ok: false, error: 'bad-upstream-host' };
|
|
100
|
+
const port = portOf(input.upstreamPort);
|
|
101
|
+
if (port === undefined)
|
|
102
|
+
return { ok: false, error: 'bad-upstream-port' };
|
|
103
|
+
return {
|
|
104
|
+
ok: true,
|
|
105
|
+
steps: [
|
|
106
|
+
'在 Lucky 的「Web 服务」里新建一条规则,监听 443 端口,绑定你的域名。',
|
|
107
|
+
`后端(反代目标)填 http://${upstream}:${port},开启 WebSocket 支持。`,
|
|
108
|
+
'打开「万事大吉」开关(放行全部路径),保存即可生效。',
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=proxy-snippets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-snippets.js","sourceRoot":"","sources":["../../src/wizard/proxy-snippets.ts"],"names":[],"mappings":"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"}
|