dsh-zen-remote 2.2.9 → 2.3.2
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.js +3293 -6
- 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 +58 -3
- package/lib/merge-streams.js +42 -12
- package/lib/merge-streams.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/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/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/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 +2224 -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/settings-form.ts +12 -3
- package/src/client-data/wizard.ts +432 -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/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,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tunnel orchestration (GW-13b, 路 C's autopilot): one call drives the
|
|
3
|
+
* whole Cloudflare-side setup the design promises — zone/account lookup
|
|
4
|
+
* (through the ancestor-chain check), tunnel create-or-adopt, ingress
|
|
5
|
+
* configuration against the gateway's ACTUAL port, the proxied CNAME,
|
|
6
|
+
* and the cloudflared child.
|
|
7
|
+
*
|
|
8
|
+
* Rules the review and the design pin down:
|
|
9
|
+
*
|
|
10
|
+
* - **已有同名隧道**:the first setup pass answers `tunnel-conflict`
|
|
11
|
+
* with the same-name list; the UI lets the user pick one and re-runs
|
|
12
|
+
* with `tunnelId`, which adopts the existing tunnel through the token
|
|
13
|
+
* endpoint instead of creating anything.
|
|
14
|
+
* - **模式切换**:starting the tunnel stops the managed Caddy first
|
|
15
|
+
* (`stopCaddy`) — never two ingress processes at once.
|
|
16
|
+
* - The connector token travels from the Cloudflare answer into the
|
|
17
|
+
* 0600 token file and the child's `--token-file`; it never reaches a
|
|
18
|
+
* response or a log.
|
|
19
|
+
* - Every failure is a VALUE with a stable `reason` the UI maps to
|
|
20
|
+
* locale copy; CloudflareError codes map losslessly (`cf-*` → the
|
|
21
|
+
* orchestration's own reasons), unknown ones degrade to `api`.
|
|
22
|
+
*/
|
|
23
|
+
import { type CloudflareApi, type CloudflareTunnelSummary } from './cloudflare.js';
|
|
24
|
+
/** The tunnel name the wizard mints and looks for. */
|
|
25
|
+
export declare const TUNNEL_NAME = "dsh-zen-remote";
|
|
26
|
+
export interface TunnelOrchestratorOptions {
|
|
27
|
+
dshHome: string;
|
|
28
|
+
/** The stored Cloudflare token (token-store); absent → `no-token`. */
|
|
29
|
+
readToken: () => string | undefined;
|
|
30
|
+
/** The gateway's ACTUAL port (the ingress service target); may probe
|
|
31
|
+
* asynchronously (the wiring re-reads /lan-gate/status per setup). */
|
|
32
|
+
gatewayPort: () => number | Promise<number>;
|
|
33
|
+
/** The client factory; injectable so the tests drive the orchestration
|
|
34
|
+
* against a scripted CloudflareApi without any HTTP. Defaults to
|
|
35
|
+
* createCloudflareApi over the injected fetch. */
|
|
36
|
+
apiFactory?: (token: string) => CloudflareApi;
|
|
37
|
+
cloudflared: {
|
|
38
|
+
start: (input: {
|
|
39
|
+
tunnelToken: string;
|
|
40
|
+
}) => Promise<{
|
|
41
|
+
ok: true;
|
|
42
|
+
} | {
|
|
43
|
+
ok: false;
|
|
44
|
+
error: string;
|
|
45
|
+
}>;
|
|
46
|
+
/** 模式切换:starting the tunnel stops the managed Caddy first. */
|
|
47
|
+
stopCaddy: () => void;
|
|
48
|
+
};
|
|
49
|
+
fetchImpl?: typeof fetch;
|
|
50
|
+
}
|
|
51
|
+
export interface TunnelSetupInput {
|
|
52
|
+
/** The public domain (bare hostname; may be a subdomain of the zone). */
|
|
53
|
+
domain: string;
|
|
54
|
+
/** Adopt this existing tunnel instead of creating one (the conflict path). */
|
|
55
|
+
tunnelId?: string;
|
|
56
|
+
}
|
|
57
|
+
export type TunnelSetupResult = {
|
|
58
|
+
ok: true;
|
|
59
|
+
tunnelId: string;
|
|
60
|
+
publicUrl: string;
|
|
61
|
+
tokenFile: string;
|
|
62
|
+
} | {
|
|
63
|
+
ok: false;
|
|
64
|
+
reason: 'no-token' | 'bad-domain' | 'zone-not-found' | 'tunnel-conflict' | 'dns-conflict' | 'auth' | 'network' | 'api' | 'spawn-failed';
|
|
65
|
+
detail?: string;
|
|
66
|
+
/** The same-name tunnels, for `tunnel-conflict`. */
|
|
67
|
+
existing?: CloudflareTunnelSummary[];
|
|
68
|
+
/** The manual-install page, for `spawn-failed`. */
|
|
69
|
+
installUrl?: string;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Drive the whole tunnel setup. Throws nothing — every outcome is a value.
|
|
73
|
+
*/
|
|
74
|
+
export declare function setupTunnel(options: TunnelOrchestratorOptions, input: TunnelSetupInput): Promise<TunnelSetupResult>;
|
|
75
|
+
/**
|
|
76
|
+
* The check-domain probe the migration card's 重新检查 drives: does any
|
|
77
|
+
* ancestor of the domain name a zone on the token's accounts?
|
|
78
|
+
*/
|
|
79
|
+
export type TunnelCheckResult = {
|
|
80
|
+
ok: true;
|
|
81
|
+
onCloudflare: boolean;
|
|
82
|
+
zoneId: string | null;
|
|
83
|
+
accountId: string | null;
|
|
84
|
+
} | {
|
|
85
|
+
ok: false;
|
|
86
|
+
reason: 'no-token' | 'bad-domain' | 'auth' | 'network' | 'api';
|
|
87
|
+
detail?: string;
|
|
88
|
+
};
|
|
89
|
+
export declare function checkDomainOnCloudflare(options: Pick<TunnelOrchestratorOptions, 'readToken' | 'fetchImpl' | 'apiFactory'>, domain: string): Promise<TunnelCheckResult>;
|
|
90
|
+
//# sourceMappingURL=tunnel-orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-orchestrator.d.ts","sourceRoot":"","sources":["../../../src/wizard/tunnel-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAAuB,KAAK,aAAa,EAAE,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAGvG,sDAAsD;AACtD,eAAO,MAAM,WAAW,mBAAmB,CAAA;AAE3C,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAA;IACf,sEAAsE;IACtE,SAAS,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;IACnC;0EACsE;IACtE,WAAW,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3C;;sDAEkD;IAClD,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,aAAa,CAAA;IAC7C,WAAW,EAAE;QACX,KAAK,EAAE,CAAC,KAAK,EAAE;YAAE,WAAW,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC;YAAE,EAAE,EAAE,IAAI,CAAA;SAAE,GAAG;YAAE,EAAE,EAAE,KAAK,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC/F,8DAA8D;QAC9D,SAAS,EAAE,MAAM,IAAI,CAAA;KACtB,CAAA;IACD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACpE;IACA,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,cAAc,CAAA;IACvI,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oDAAoD;IACpD,QAAQ,CAAC,EAAE,uBAAuB,EAAE,CAAA;IACpC,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAcH;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,yBAAyB,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwFzH;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACpF;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAElG,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,yBAAyB,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAe5K"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Final-verification orchestration (GW-9): the wizard's last mile, where
|
|
3
|
+
* 「完成」 means a real phone really paired through the real public URL —
|
|
4
|
+
* never a green light from internal checks alone (设计: 不做已联通的假绿灯).
|
|
5
|
+
*
|
|
6
|
+
* The flow, spread over two entry points the UI drives:
|
|
7
|
+
*
|
|
8
|
+
* start({ publicUrl }): runtime ready? → walk the public URL
|
|
9
|
+
* (GW-8's probe; BOTH the pairing page and the admin-403 shape must be
|
|
10
|
+
* right) → snapshot the gateway's device ids (the baseline) → mint a WEB
|
|
11
|
+
* pairing code → remember the session IN MEMORY and answer with the
|
|
12
|
+
* code, the URL and the expiry. The code never touches the row config,
|
|
13
|
+
* and minting it restarts nothing — trustedProxies changes are what
|
|
14
|
+
* restart the gateway, and any such change invalidates the session
|
|
15
|
+
* instead (the config fingerprint below).
|
|
16
|
+
*
|
|
17
|
+
* check(): re-read the gateway status. A device id the baseline did not
|
|
18
|
+
* have is a freshly paired phone; the gateway's lastWebClaim record must
|
|
19
|
+
* name THAT device with secure === true (the proxy passed
|
|
20
|
+
* X-Forwarded-Proto: https through, so the issued cookie carries the
|
|
21
|
+
* Secure flag). All green → pass. The claim recorded an insecure cookie →
|
|
22
|
+
* cookie-insecure. Past the code's expiry → code-expired. Anything else →
|
|
23
|
+
* pending, keep polling.
|
|
24
|
+
*
|
|
25
|
+
* The session binds to a CONFIG FINGERPRINT (ingressMode, domain,
|
|
26
|
+
* httpsPort, publicUrl, the gateway's ACTUAL port, host, trustedProxies):
|
|
27
|
+
* any of those moving between start and check answers stale and discards
|
|
28
|
+
* the session, because the verification proved a URL that no longer exists.
|
|
29
|
+
* The session is deliberately memory-only — a row reload (which a
|
|
30
|
+
* trustedProxies change causes) builds a fresh manager and the session is
|
|
31
|
+
* gone, exactly the same verdict the fingerprint would have produced.
|
|
32
|
+
*/
|
|
33
|
+
import type { ZenRemoteConfig } from '../config.js';
|
|
34
|
+
import { type HostPathOutcome } from './probe.js';
|
|
35
|
+
export interface VerificationManagerOptions {
|
|
36
|
+
/** Loopback base of the gateway child, `http://127.0.0.1:<port>`. */
|
|
37
|
+
gatewayBase: string;
|
|
38
|
+
/** Fresh resolved config values per call (volatile discipline). */
|
|
39
|
+
getValues: () => ZenRemoteConfig;
|
|
40
|
+
/** The hostPath probe; injectable for tests (defaults to GW-8's real one). */
|
|
41
|
+
probe?: (publicUrl: string) => Promise<HostPathOutcome>;
|
|
42
|
+
/** Whether the ingress processes are alive (Caddy / cloudflared). The
|
|
43
|
+
* wiring supplies this once the process managers are mounted; absent
|
|
44
|
+
* means "checked elsewhere" and only the gateway is probed. */
|
|
45
|
+
runtimeReady?: () => boolean | Promise<boolean>;
|
|
46
|
+
fetchImpl?: typeof fetch;
|
|
47
|
+
/** Per-gateway-call ceiling. Default 5000 ms. */
|
|
48
|
+
timeoutMs?: number;
|
|
49
|
+
/** Clock (tests drive expiry). */
|
|
50
|
+
now?: () => number;
|
|
51
|
+
}
|
|
52
|
+
export type VerificationStartResult = {
|
|
53
|
+
ok: true;
|
|
54
|
+
pairingCode: string;
|
|
55
|
+
publicUrl: string;
|
|
56
|
+
expiresAt: number;
|
|
57
|
+
} | {
|
|
58
|
+
ok: false;
|
|
59
|
+
reason: 'no-public-url' | 'bad-public-url' | 'runtime-not-ready' | 'gateway-unreachable' | 'probe-failed' | 'pair-refused';
|
|
60
|
+
/** The probe's classifications for `probe-failed`; the gateway's status
|
|
61
|
+
* code for `pair-refused`; the normalize failure reason for
|
|
62
|
+
* `bad-public-url`. */
|
|
63
|
+
detail?: unknown;
|
|
64
|
+
};
|
|
65
|
+
export type VerificationCheckResult = {
|
|
66
|
+
result: 'not-started';
|
|
67
|
+
} | {
|
|
68
|
+
result: 'stale';
|
|
69
|
+
} | {
|
|
70
|
+
result: 'code-expired';
|
|
71
|
+
} | {
|
|
72
|
+
result: 'pending';
|
|
73
|
+
} | {
|
|
74
|
+
result: 'cookie-insecure';
|
|
75
|
+
deviceId: string;
|
|
76
|
+
} | {
|
|
77
|
+
result: 'pass';
|
|
78
|
+
deviceId: string;
|
|
79
|
+
at: number;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* The config fingerprint a session binds to: any drift between start and
|
|
83
|
+
* check means the verified URL is no longer the deployed URL.
|
|
84
|
+
*/
|
|
85
|
+
export declare function verificationFingerprint(values: ZenRemoteConfig, gatewayPort: number | null): string;
|
|
86
|
+
export interface VerificationManager {
|
|
87
|
+
start(input: {
|
|
88
|
+
publicUrl: unknown;
|
|
89
|
+
probeOnly?: boolean;
|
|
90
|
+
}): Promise<VerificationStartResult>;
|
|
91
|
+
check(): Promise<VerificationCheckResult>;
|
|
92
|
+
/** Whether a verification session is open (the route surfaces it for the
|
|
93
|
+
* UI's 「待外部验证」 state). */
|
|
94
|
+
hasSession(): boolean;
|
|
95
|
+
}
|
|
96
|
+
export declare function createVerificationManager(options: VerificationManagerOptions): VerificationManager;
|
|
97
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/wizard/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAEnD,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAA;AAEhE,MAAM,WAAW,0BAA0B;IACzC,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAA;IACnB,mEAAmE;IACnE,SAAS,EAAE,MAAM,eAAe,CAAA;IAChC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,CAAC,CAAA;IACvD;;mEAE+D;IAC/D,YAAY,CAAC,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvE;IACA,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EAAE,eAAe,GAAG,gBAAgB,GAAG,mBAAmB,GAAG,qBAAqB,GAAG,cAAc,GAAG,cAAc,CAAA;IAC1H;;2BAEuB;IACvB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAEH,MAAM,MAAM,uBAAuB,GAC/B;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,GACzB;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,MAAM,EAAE,cAAc,CAAA;CAAE,GAC1B;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,iBAAiB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AASpD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAUnG;AAWD,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAA;IAC3F,KAAK,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAA;IACzC;8BAC0B;IAC1B,UAAU,IAAI,OAAO,CAAA;CACtB;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,mBAAmB,CAyHlG"}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingress binary I/O (GW-2, revised by GW-fix-1/H1): download, unpack,
|
|
3
|
+
* install and cache-probe for the two wizard-managed binaries. The
|
|
4
|
+
* arithmetic lives in binary.ts; this file is the only one allowed to touch
|
|
5
|
+
* the filesystem or the network for it.
|
|
6
|
+
*
|
|
7
|
+
* House rules the tasks pin down:
|
|
8
|
+
*
|
|
9
|
+
* - No shell, no `child_process` at all. Downloads ride `fetch`, the
|
|
10
|
+
* body streams straight to a `.part` file, and the install is a POSIX
|
|
11
|
+
* rename — a crash mid-download can never leave a half binary at the
|
|
12
|
+
* install path.
|
|
13
|
+
* - The archive wrapper comes from binary.ts's METADATA (`options.archive`,
|
|
14
|
+
* what binaryUrls() models per platform), never from sniffing the URL:
|
|
15
|
+
* the Caddy build API's URL is a query string whose ending says nothing
|
|
16
|
+
* about the tar.gz / zip payload it answers with (GW-fix-1/H1).
|
|
17
|
+
* - Windows' Caddy asset is a zip and there is no zip reader here yet:
|
|
18
|
+
* refused up front with the typed `windows-zip-unsupported` code, the
|
|
19
|
+
* manual-install link stays the fallback (GW-fix-1/H1 item 3).
|
|
20
|
+
* - After unpacking (or for a bare download), a unix image must carry an
|
|
21
|
+
* ELF or Mach-O magic; anything else is refused as `bad-binary-image`
|
|
22
|
+
* and leaves nothing on disk (GW-fix-1/H1 item 4). Windows keeps no
|
|
23
|
+
* magic check — PE inspection is not this module's job.
|
|
24
|
+
* - The darwin cloudflared asset is a `.tgz`; it is unpacked with
|
|
25
|
+
* `zlib.gunzipSync` plus a minimal ustar reader (the archive carries a
|
|
26
|
+
* single entry — no PAX/GNU long-name handling needed, but those
|
|
27
|
+
* header types are skipped correctly if an upstream rebuild adds one).
|
|
28
|
+
* - Installed images get 0755; `ensureBinary` is idempotent (an existing
|
|
29
|
+
* executable short-circuits before any network traffic).
|
|
30
|
+
*
|
|
31
|
+
* `fetch` is injectable so the tests drive real downloads against a local
|
|
32
|
+
* mock server without touching the network.
|
|
33
|
+
*/
|
|
34
|
+
import { accessSync, chmodSync, closeSync, constants as fsConstants, createWriteStream, mkdirSync, openSync, readFileSync, readSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
35
|
+
import { Readable, Transform } from 'node:stream';
|
|
36
|
+
import { pipeline } from 'node:stream/promises';
|
|
37
|
+
import { gunzipSync } from 'node:zlib';
|
|
38
|
+
import { MANUAL_INSTALL_LINKS, binaryDownloadCandidates, binaryInstallPath, binDir, binaryUrls, currentPlatform, } from './binary.js';
|
|
39
|
+
/** Generous ceiling on one download; a mirror gone wrong must not be able
|
|
40
|
+
* to fill the disk. Both real binaries sit far below 200 MB. */
|
|
41
|
+
const MAX_DOWNLOAD_BYTES = 512 * 1024 * 1024;
|
|
42
|
+
/** Connection / response-headers timeout before a mirror is skipped. */
|
|
43
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
44
|
+
/** Maximum idle gap between body chunks before a stalled mirror is aborted. */
|
|
45
|
+
const STALL_TIMEOUT_MS = 15_000;
|
|
46
|
+
/** ELF: `\x7fELF`. */
|
|
47
|
+
const ELF_MAGIC = Buffer.from([0x7f, 0x45, 0x4c, 0x46]);
|
|
48
|
+
/** Mach-O 32 big-endian, Mach-O 64 big-endian, Mach-O 64 little-endian —
|
|
49
|
+
* the three shapes Apple toolchains emit for this codebase's targets. */
|
|
50
|
+
const MACH_O_MAGICS = [
|
|
51
|
+
Buffer.from([0xfe, 0xed, 0xfa, 0xce]),
|
|
52
|
+
Buffer.from([0xfe, 0xed, 0xfa, 0xcf]),
|
|
53
|
+
Buffer.from([0xcf, 0xfa, 0xed, 0xfe]),
|
|
54
|
+
];
|
|
55
|
+
/** Whether the first bytes of an image look like an executable this module
|
|
56
|
+
* is willing to install on a unix host (GW-fix-1/H1 item 4). */
|
|
57
|
+
function looksLikeUnixImage(head) {
|
|
58
|
+
return ELF_MAGIC.equals(head) || MACH_O_MAGICS.some((magic) => magic.equals(head));
|
|
59
|
+
}
|
|
60
|
+
/** The first four bytes of a file (fewer when the file is shorter). */
|
|
61
|
+
function headBytes(path) {
|
|
62
|
+
const head = Buffer.alloc(4);
|
|
63
|
+
const fd = openSync(path, 'r');
|
|
64
|
+
try {
|
|
65
|
+
return head.subarray(0, readSync(fd, head, 0, 4, 0));
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
closeSync(fd);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* One failed or refused download/install. `code` is the wire-stable marker
|
|
73
|
+
* the UI classifies on — `'download-failed'` for transport/shaping
|
|
74
|
+
* failures, `'windows-zip-unsupported'` for the zip refusal, and
|
|
75
|
+
* `'bad-binary-image'` when the payload did not carry an executable
|
|
76
|
+
* image's magic. `installUrl` is the official manual-install page the
|
|
77
|
+
* failure card links to. Fields are assigned in the constructor body rather
|
|
78
|
+
* than declared as parameter properties (the check scripts import modules
|
|
79
|
+
* through Node's strip-only type stripping, which rejects that syntax).
|
|
80
|
+
*/
|
|
81
|
+
export class BinaryDownloadError extends Error {
|
|
82
|
+
code;
|
|
83
|
+
tool;
|
|
84
|
+
url;
|
|
85
|
+
status;
|
|
86
|
+
installUrl;
|
|
87
|
+
constructor(tool, url, status, message, options) {
|
|
88
|
+
super(message, options);
|
|
89
|
+
this.name = 'BinaryDownloadError';
|
|
90
|
+
this.code = options?.code ?? 'download-failed';
|
|
91
|
+
this.tool = tool;
|
|
92
|
+
this.url = url;
|
|
93
|
+
this.status = status;
|
|
94
|
+
this.installUrl = MANUAL_INSTALL_LINKS[tool];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Whether `path` exists and is executable (the "already installed" test). */
|
|
98
|
+
function isExecutableFile(path) {
|
|
99
|
+
try {
|
|
100
|
+
if (!statSync(path).isFile())
|
|
101
|
+
return false;
|
|
102
|
+
accessSync(path, fsConstants.X_OK);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The installed binary's absolute path, or undefined when it is not (yet)
|
|
111
|
+
* on disk in executable shape — the read-only probe; see binary.ts's
|
|
112
|
+
* `binaryInstallPath` for the pure arithmetic.
|
|
113
|
+
*/
|
|
114
|
+
export function binaryPath(dshHome, tool, platform = currentPlatform()) {
|
|
115
|
+
const path = binaryInstallPath(dshHome, tool, platform);
|
|
116
|
+
return isExecutableFile(path) ? path : undefined;
|
|
117
|
+
}
|
|
118
|
+
/** Write one fetched response body to `partPath` under the size cap and
|
|
119
|
+
* per-chunk stall timeout so a throttled or hung mirror fails over quickly. */
|
|
120
|
+
async function streamToFile(tool, url, body, partPath, onProgress) {
|
|
121
|
+
let received = 0;
|
|
122
|
+
let stallTimer;
|
|
123
|
+
const counter = new Transform({
|
|
124
|
+
transform(chunk, _encoding, done) {
|
|
125
|
+
received += chunk.length;
|
|
126
|
+
if (received > MAX_DOWNLOAD_BYTES) {
|
|
127
|
+
done(new BinaryDownloadError(tool, url, null, `the download of ${tool} exceeds the ${MAX_DOWNLOAD_BYTES}-byte cap`));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (stallTimer !== undefined)
|
|
131
|
+
clearTimeout(stallTimer);
|
|
132
|
+
stallTimer = setTimeout(() => {
|
|
133
|
+
counter.destroy(new BinaryDownloadError(tool, url, null, `the download of ${tool} stalled after ${received} bytes`));
|
|
134
|
+
}, STALL_TIMEOUT_MS);
|
|
135
|
+
if (typeof stallTimer.unref === 'function')
|
|
136
|
+
stallTimer.unref();
|
|
137
|
+
onProgress?.(received, null);
|
|
138
|
+
done(null, chunk);
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
stallTimer = setTimeout(() => {
|
|
142
|
+
counter.destroy(new BinaryDownloadError(tool, url, null, `the download of ${tool} stalled before first byte`));
|
|
143
|
+
}, STALL_TIMEOUT_MS);
|
|
144
|
+
if (typeof stallTimer.unref === 'function')
|
|
145
|
+
stallTimer.unref();
|
|
146
|
+
try {
|
|
147
|
+
await pipeline(Readable.fromWeb(body), counter, createWriteStream(partPath, { mode: 0o644 }));
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
rmSync(partPath, { force: true });
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
if (stallTimer !== undefined)
|
|
155
|
+
clearTimeout(stallTimer);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** Size bytes of a ustar header's octal size field, 0 when unparsable. */
|
|
159
|
+
function tarEntrySize(header) {
|
|
160
|
+
const raw = header.subarray(124, 136).toString('ascii').replace(/[\0 ]+$/g, '').trim();
|
|
161
|
+
const size = parseInt(raw === '' ? '0' : raw, 8);
|
|
162
|
+
return Number.isFinite(size) && size > 0 ? size : 0;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The first regular-file payload of a ustar archive as one buffer slice.
|
|
166
|
+
* The cloudflared `.tgz` carries exactly one (`cloudflared`); anything else
|
|
167
|
+
* (PAX/GNU metadata, directories) is skipped by advancing over its data
|
|
168
|
+
* blocks, so a future archive layout change degrades to "not found" instead
|
|
169
|
+
* of corrupt bytes.
|
|
170
|
+
*/
|
|
171
|
+
function firstTarFileEntry(archive) {
|
|
172
|
+
let offset = 0;
|
|
173
|
+
while (offset + 512 <= archive.length) {
|
|
174
|
+
const header = archive.subarray(offset, offset + 512);
|
|
175
|
+
if (header.every((byte) => byte === 0))
|
|
176
|
+
return undefined;
|
|
177
|
+
const size = tarEntrySize(header);
|
|
178
|
+
const typeflag = String.fromCharCode(header[156] ?? 0x20);
|
|
179
|
+
const dataStart = offset + 512;
|
|
180
|
+
if ((typeflag === '0' || typeflag === '\0') && size > 0 && dataStart + size <= archive.length) {
|
|
181
|
+
return archive.subarray(dataStart, dataStart + size);
|
|
182
|
+
}
|
|
183
|
+
offset = dataStart + Math.ceil(size / 512) * 512;
|
|
184
|
+
}
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
function chmodExec(path) {
|
|
188
|
+
try {
|
|
189
|
+
chmodSync(path, 0o755);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Windows: no executable bit — the extension does that job.
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** Install `bytes` at `installPath`: `.part` sibling, chmod, atomic rename. */
|
|
196
|
+
function installBytes(bytes, installPath) {
|
|
197
|
+
const partPath = `${installPath}.part`;
|
|
198
|
+
try {
|
|
199
|
+
writeFileSync(partPath, bytes, { mode: 0o644 });
|
|
200
|
+
chmodExec(partPath);
|
|
201
|
+
renameSync(partPath, installPath);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
rmSync(partPath, { force: true });
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** The unix image-magic gate (GW-fix-1/H1 item 4): a payload that does not
|
|
209
|
+
* open with an ELF or Mach-O magic is refused, nothing lands on disk. */
|
|
210
|
+
function assertUnixImageMagic(tool, url, status, head) {
|
|
211
|
+
if (looksLikeUnixImage(head))
|
|
212
|
+
return;
|
|
213
|
+
throw new BinaryDownloadError(tool, url, status, `the downloaded ${tool} does not carry an ELF or Mach-O image magic`, { code: 'bad-binary-image' });
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Download one binary from an explicit URL and install it. The archive
|
|
217
|
+
* wrapper is the CALLER'S metadata (`options.archive`) — `ensureBinary`
|
|
218
|
+
* feeds it from binaryUrls(); tests pin it per fixture. `platform` only
|
|
219
|
+
* matters for the install name and the magic-check gate; it defaults to
|
|
220
|
+
* this process's.
|
|
221
|
+
* @returns the absolute install path.
|
|
222
|
+
*/
|
|
223
|
+
export async function downloadBinary(dshHome, tool, url, options = {}) {
|
|
224
|
+
const platform = options.platform ?? currentPlatform();
|
|
225
|
+
const archive = options.archive ?? 'none';
|
|
226
|
+
const installPath = binaryInstallPath(dshHome, tool, platform);
|
|
227
|
+
if (archive === 'zip') {
|
|
228
|
+
// Refused BEFORE any network traffic: there is no zip reader in this
|
|
229
|
+
// codebase yet (GW-fix-1/H1 item 3), and half-supporting the format
|
|
230
|
+
// would only install a zip's bytes as an image.
|
|
231
|
+
throw new BinaryDownloadError(tool, url, null, `the ${tool} asset for Windows ships as a zip, which cannot be unpacked here yet`, { code: 'windows-zip-unsupported' });
|
|
232
|
+
}
|
|
233
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
234
|
+
let response;
|
|
235
|
+
try {
|
|
236
|
+
response = await fetchImpl(url, {
|
|
237
|
+
redirect: 'follow',
|
|
238
|
+
signal: AbortSignal.timeout(CONNECT_TIMEOUT_MS),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
catch (cause) {
|
|
242
|
+
throw new BinaryDownloadError(tool, url, null, `the download of ${tool} failed before an answer arrived`, { cause });
|
|
243
|
+
}
|
|
244
|
+
if (!response.ok || response.body === null) {
|
|
245
|
+
throw new BinaryDownloadError(tool, url, response.status, `the download of ${tool} answered ${response.status}`);
|
|
246
|
+
}
|
|
247
|
+
mkdirSync(binDir(dshHome), { recursive: true });
|
|
248
|
+
const declared = Number(response.headers.get('content-length'));
|
|
249
|
+
const total = Number.isFinite(declared) && declared > 0 ? declared : null;
|
|
250
|
+
const onProgress = options.onProgress
|
|
251
|
+
? (received) => options.onProgress?.(received, total)
|
|
252
|
+
: undefined;
|
|
253
|
+
if (archive === 'tgz') {
|
|
254
|
+
// Archive (or auto-detected bare binary if an upstream endpoint serves a
|
|
255
|
+
// raw ELF/Mach-O image despite tgz metadata): land the bytes in a sibling,
|
|
256
|
+
// then either install directly when the payload already starts with a
|
|
257
|
+
// unix image magic or gunzip + extract the tar entry.
|
|
258
|
+
const tgzPath = `${installPath}.download.tgz`;
|
|
259
|
+
try {
|
|
260
|
+
await streamToFile(tool, url, response.body, tgzPath, onProgress);
|
|
261
|
+
const rawBytes = readFileSync(tgzPath);
|
|
262
|
+
const gzipMagic = rawBytes.length >= 2 && rawBytes[0] === 0x1f && rawBytes[1] === 0x8b;
|
|
263
|
+
const entry = (!gzipMagic && platform.os !== 'windows' && looksLikeUnixImage(rawBytes.subarray(0, 4)))
|
|
264
|
+
? rawBytes
|
|
265
|
+
: firstTarFileEntry(gunzipSync(rawBytes));
|
|
266
|
+
if (entry === undefined) {
|
|
267
|
+
throw new BinaryDownloadError(tool, url, response.status, `the ${tool} archive carried no binary entry`);
|
|
268
|
+
}
|
|
269
|
+
if (platform.os !== 'windows')
|
|
270
|
+
assertUnixImageMagic(tool, url, response.status, entry.subarray(0, 4));
|
|
271
|
+
installBytes(entry, installPath);
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
if (error instanceof BinaryDownloadError)
|
|
275
|
+
throw error;
|
|
276
|
+
throw new BinaryDownloadError(tool, url, response.status, `the ${tool} archive could not be unpacked`, { cause: error });
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
rmSync(tgzPath, { force: true });
|
|
280
|
+
}
|
|
281
|
+
return installPath;
|
|
282
|
+
}
|
|
283
|
+
// Bare executable: stream to the .part, gate the magic, then rename.
|
|
284
|
+
const partPath = `${installPath}.part`;
|
|
285
|
+
try {
|
|
286
|
+
await streamToFile(tool, url, response.body, partPath, onProgress);
|
|
287
|
+
if (platform.os !== 'windows')
|
|
288
|
+
assertUnixImageMagic(tool, url, response.status, headBytes(partPath));
|
|
289
|
+
chmodExec(partPath);
|
|
290
|
+
renameSync(partPath, installPath);
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
rmSync(partPath, { force: true });
|
|
294
|
+
if (error instanceof BinaryDownloadError)
|
|
295
|
+
throw error;
|
|
296
|
+
throw new BinaryDownloadError(tool, url, response.status, `the download of ${tool} could not be written to disk`, { cause: error });
|
|
297
|
+
}
|
|
298
|
+
return installPath;
|
|
299
|
+
}
|
|
300
|
+
/** Cached mainland-China network detection so repeated downloads do not
|
|
301
|
+
* re-probe within the same process. */
|
|
302
|
+
let cachedCnNetwork;
|
|
303
|
+
/**
|
|
304
|
+
* Detect whether the current host is in mainland China:
|
|
305
|
+
* 1. Fast local check: system timezone is `Asia/Shanghai`, `Asia/Chongqing`,
|
|
306
|
+
* `Asia/Harbin`, or `Asia/Urumqi`, or `LANG`/`LC_ALL` carries `zh_CN`.
|
|
307
|
+
* 2. Quick 1.5s probe to Cloudflare's `/cdn-cgi/trace` (`loc=CN`), or if
|
|
308
|
+
* direct `github.com` HEAD fails/times out within 2s.
|
|
309
|
+
*/
|
|
310
|
+
export async function detectCnNetwork(fetchImpl = fetch) {
|
|
311
|
+
if (cachedCnNetwork !== undefined)
|
|
312
|
+
return cachedCnNetwork;
|
|
313
|
+
try {
|
|
314
|
+
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone ?? '';
|
|
315
|
+
if (/^Asia\/(Shanghai|Chongqing|Harbin|Urumqi)$/i.test(tz)) {
|
|
316
|
+
cachedCnNetwork = true;
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch { /* ignore */ }
|
|
321
|
+
const envLang = `${process.env.LANG ?? ''} ${process.env.LC_ALL ?? ''}`;
|
|
322
|
+
if (/zh_CN/i.test(envLang)) {
|
|
323
|
+
cachedCnNetwork = true;
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
const trace = await fetchImpl('https://www.cloudflare.com/cdn-cgi/trace', {
|
|
328
|
+
signal: AbortSignal.timeout(1500),
|
|
329
|
+
});
|
|
330
|
+
if (trace.ok) {
|
|
331
|
+
const text = await trace.text();
|
|
332
|
+
if (/\bloc=CN\b/i.test(text)) {
|
|
333
|
+
cachedCnNetwork = true;
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
cachedCnNetwork = false;
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
// ignore trace timeout
|
|
342
|
+
}
|
|
343
|
+
cachedCnNetwork = false;
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* The idempotent front door: an already-installed executable short-circuits
|
|
348
|
+
* (no network), anything else downloads from the official distribution
|
|
349
|
+
* point for the tool, archive shape included.
|
|
350
|
+
*
|
|
351
|
+
* When using the default `fetch` transport, automatically detects mainland
|
|
352
|
+
* China network environments and tries prebuilt GitHub Release mirrors
|
|
353
|
+
* (`gh-proxy.com` / `ghfast.top`) + direct GitHub Release before falling back
|
|
354
|
+
* to `caddyserver.com/api/download`. When a custom `fetchImpl` is injected
|
|
355
|
+
* (unit tests), calls the canonical `binaryUrls(platform)` endpoint directly.
|
|
356
|
+
* @returns the absolute install path and whether THIS call downloaded.
|
|
357
|
+
*/
|
|
358
|
+
export async function ensureBinary(dshHome, tool, options = {}) {
|
|
359
|
+
const platform = options.platform ?? currentPlatform();
|
|
360
|
+
const existing = binaryPath(dshHome, tool, platform);
|
|
361
|
+
if (existing !== undefined)
|
|
362
|
+
return { path: existing, downloaded: false };
|
|
363
|
+
if (options.fetchImpl !== undefined) {
|
|
364
|
+
const urls = binaryUrls(platform);
|
|
365
|
+
const [url, archive] = tool === 'caddy' ? [urls.caddy, urls.caddyArchive] : [urls.cloudflared, urls.cloudflaredArchive];
|
|
366
|
+
const path = await downloadBinary(dshHome, tool, url, { ...options, archive });
|
|
367
|
+
return { path, downloaded: true };
|
|
368
|
+
}
|
|
369
|
+
const preferCnMirror = await detectCnNetwork();
|
|
370
|
+
const candidates = binaryDownloadCandidates(tool, platform, preferCnMirror);
|
|
371
|
+
let lastError;
|
|
372
|
+
for (const candidate of candidates) {
|
|
373
|
+
try {
|
|
374
|
+
const path = await downloadBinary(dshHome, tool, candidate.url, {
|
|
375
|
+
...options,
|
|
376
|
+
archive: candidate.archive,
|
|
377
|
+
});
|
|
378
|
+
return { path, downloaded: true };
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
lastError = error;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
throw lastError;
|
|
385
|
+
}
|
|
386
|
+
//# sourceMappingURL=binary-io.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-io.js","sourceRoot":"","sources":["../../src/wizard/binary-io.ts"],"names":[],"mappings":"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"}
|