dsh-zen-remote 2.2.9 → 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.
Files changed (149) hide show
  1. package/README.md +2 -2
  2. package/README.zh-CN.md +2 -2
  3. package/docs/interface.md +1 -1
  4. package/docs/remote-access.md +20 -0
  5. package/lan-gate.mjs +11 -1
  6. package/lib/adapters/dsh-better-sidebar/index.js +6 -2
  7. package/lib/adapters/dsh-better-sidebar/index.js.map +1 -1
  8. package/lib/adapters/dsh-better-sidebar/methods.js +3 -0
  9. package/lib/adapters/dsh-better-sidebar/methods.js.map +1 -1
  10. package/lib/admin-routes.js +33 -2
  11. package/lib/admin-routes.js.map +1 -1
  12. package/lib/client.js +3289 -6
  13. package/lib/config.js +70 -0
  14. package/lib/config.js.map +1 -1
  15. package/lib/index.js +232 -2
  16. package/lib/index.js.map +1 -1
  17. package/lib/intercept.js +43 -3
  18. package/lib/intercept.js.map +1 -1
  19. package/lib/lan-gate-server.cjs +29 -3
  20. package/lib/merge-streams.js +42 -12
  21. package/lib/merge-streams.js.map +1 -1
  22. package/lib/types/adapters/dsh-better-sidebar/index.d.ts +1 -1
  23. package/lib/types/adapters/dsh-better-sidebar/index.d.ts.map +1 -1
  24. package/lib/types/adapters/dsh-better-sidebar/methods.d.ts.map +1 -1
  25. package/lib/types/admin-routes.d.ts +13 -0
  26. package/lib/types/admin-routes.d.ts.map +1 -1
  27. package/lib/types/client/index.d.ts.map +1 -1
  28. package/lib/types/client/locales.d.ts +204 -1
  29. package/lib/types/client/locales.d.ts.map +1 -1
  30. package/lib/types/client/wizard/GatewayWizard.d.ts +29 -0
  31. package/lib/types/client/wizard/GatewayWizard.d.ts.map +1 -0
  32. package/lib/types/client/wizard/WizardCard.d.ts +30 -0
  33. package/lib/types/client/wizard/WizardCard.d.ts.map +1 -0
  34. package/lib/types/client/wizard/cards/MigrateCard.d.ts +15 -0
  35. package/lib/types/client/wizard/cards/MigrateCard.d.ts.map +1 -0
  36. package/lib/types/client/wizard/cards/PortForwardCard.d.ts +15 -0
  37. package/lib/types/client/wizard/cards/PortForwardCard.d.ts.map +1 -0
  38. package/lib/types/client/wizard/cards/ProxyCard.d.ts +19 -0
  39. package/lib/types/client/wizard/cards/ProxyCard.d.ts.map +1 -0
  40. package/lib/types/client/wizard/cards/TokenCard.d.ts +20 -0
  41. package/lib/types/client/wizard/cards/TokenCard.d.ts.map +1 -0
  42. package/lib/types/client/wizard/register-wizard.d.ts +51 -0
  43. package/lib/types/client/wizard/register-wizard.d.ts.map +1 -0
  44. package/lib/types/client/wizard/wizard-css.d.ts +10 -0
  45. package/lib/types/client/wizard/wizard-css.d.ts.map +1 -0
  46. package/lib/types/client-data/qr.d.ts +20 -0
  47. package/lib/types/client-data/qr.d.ts.map +1 -0
  48. package/lib/types/client-data/settings-form.d.ts +7 -2
  49. package/lib/types/client-data/settings-form.d.ts.map +1 -1
  50. package/lib/types/client-data/wizard.d.ts +247 -0
  51. package/lib/types/client-data/wizard.d.ts.map +1 -0
  52. package/lib/types/config.d.ts +27 -0
  53. package/lib/types/config.d.ts.map +1 -1
  54. package/lib/types/index.d.ts.map +1 -1
  55. package/lib/types/intercept.d.ts.map +1 -1
  56. package/lib/types/merge-streams.d.ts.map +1 -1
  57. package/lib/types/wizard/binary-io.d.ts +111 -0
  58. package/lib/types/wizard/binary-io.d.ts.map +1 -0
  59. package/lib/types/wizard/binary.d.ts +118 -0
  60. package/lib/types/wizard/binary.d.ts.map +1 -0
  61. package/lib/types/wizard/caddy-process.d.ts +101 -0
  62. package/lib/types/wizard/caddy-process.d.ts.map +1 -0
  63. package/lib/types/wizard/caddy.d.ts +53 -0
  64. package/lib/types/wizard/caddy.d.ts.map +1 -0
  65. package/lib/types/wizard/cloudflare.d.ts +112 -0
  66. package/lib/types/wizard/cloudflare.d.ts.map +1 -0
  67. package/lib/types/wizard/cloudflared.d.ts +71 -0
  68. package/lib/types/wizard/cloudflared.d.ts.map +1 -0
  69. package/lib/types/wizard/probe.d.ts +66 -0
  70. package/lib/types/wizard/probe.d.ts.map +1 -0
  71. package/lib/types/wizard/proxy-snippets.d.ts +60 -0
  72. package/lib/types/wizard/proxy-snippets.d.ts.map +1 -0
  73. package/lib/types/wizard/routes.d.ts +152 -0
  74. package/lib/types/wizard/routes.d.ts.map +1 -0
  75. package/lib/types/wizard/source-detect.d.ts +53 -0
  76. package/lib/types/wizard/source-detect.d.ts.map +1 -0
  77. package/lib/types/wizard/status.d.ts +115 -0
  78. package/lib/types/wizard/status.d.ts.map +1 -0
  79. package/lib/types/wizard/token-store.d.ts +27 -0
  80. package/lib/types/wizard/token-store.d.ts.map +1 -0
  81. package/lib/types/wizard/tunnel-orchestrator.d.ts +90 -0
  82. package/lib/types/wizard/tunnel-orchestrator.d.ts.map +1 -0
  83. package/lib/types/wizard/verify.d.ts +97 -0
  84. package/lib/types/wizard/verify.d.ts.map +1 -0
  85. package/lib/wizard/binary-io.js +386 -0
  86. package/lib/wizard/binary-io.js.map +1 -0
  87. package/lib/wizard/binary.js +138 -0
  88. package/lib/wizard/binary.js.map +1 -0
  89. package/lib/wizard/caddy-process.js +162 -0
  90. package/lib/wizard/caddy-process.js.map +1 -0
  91. package/lib/wizard/caddy.js +80 -0
  92. package/lib/wizard/caddy.js.map +1 -0
  93. package/lib/wizard/cloudflare.js +334 -0
  94. package/lib/wizard/cloudflare.js.map +1 -0
  95. package/lib/wizard/cloudflared.js +158 -0
  96. package/lib/wizard/cloudflared.js.map +1 -0
  97. package/lib/wizard/probe.js +118 -0
  98. package/lib/wizard/probe.js.map +1 -0
  99. package/lib/wizard/proxy-snippets.js +112 -0
  100. package/lib/wizard/proxy-snippets.js.map +1 -0
  101. package/lib/wizard/routes.js +462 -0
  102. package/lib/wizard/routes.js.map +1 -0
  103. package/lib/wizard/source-detect.js +77 -0
  104. package/lib/wizard/source-detect.js.map +1 -0
  105. package/lib/wizard/status.js +94 -0
  106. package/lib/wizard/status.js.map +1 -0
  107. package/lib/wizard/token-store.js +71 -0
  108. package/lib/wizard/token-store.js.map +1 -0
  109. package/lib/wizard/tunnel-orchestrator.js +155 -0
  110. package/lib/wizard/tunnel-orchestrator.js.map +1 -0
  111. package/lib/wizard/verify.js +171 -0
  112. package/lib/wizard/verify.js.map +1 -0
  113. package/package.json +37 -37
  114. package/scripts/check-settings-form.mjs +5 -4
  115. package/scripts/check-wizard-ui.mjs +690 -0
  116. package/src/adapters/dsh-better-sidebar/index.ts +6 -2
  117. package/src/adapters/dsh-better-sidebar/methods.ts +3 -0
  118. package/src/admin-routes.ts +44 -2
  119. package/src/client/index.tsx +9 -0
  120. package/src/client/locales.ts +410 -2
  121. package/src/client/wizard/GatewayWizard.tsx +2220 -0
  122. package/src/client/wizard/WizardCard.tsx +81 -0
  123. package/src/client/wizard/cards/MigrateCard.tsx +42 -0
  124. package/src/client/wizard/cards/PortForwardCard.tsx +37 -0
  125. package/src/client/wizard/cards/ProxyCard.tsx +56 -0
  126. package/src/client/wizard/cards/TokenCard.tsx +81 -0
  127. package/src/client/wizard/register-wizard.ts +99 -0
  128. package/src/client/wizard/wizard-css.ts +948 -0
  129. package/src/client-data/qr.ts +284 -0
  130. package/src/client-data/settings-form.ts +12 -3
  131. package/src/client-data/wizard.ts +432 -0
  132. package/src/config.ts +87 -0
  133. package/src/index.ts +227 -2
  134. package/src/intercept.ts +43 -3
  135. package/src/merge-streams.ts +41 -12
  136. package/src/wizard/binary-io.ts +449 -0
  137. package/src/wizard/binary.ts +177 -0
  138. package/src/wizard/caddy-process.ts +228 -0
  139. package/src/wizard/caddy.ts +97 -0
  140. package/src/wizard/cloudflare.ts +413 -0
  141. package/src/wizard/cloudflared.ts +196 -0
  142. package/src/wizard/probe.ts +161 -0
  143. package/src/wizard/proxy-snippets.ts +128 -0
  144. package/src/wizard/routes.ts +537 -0
  145. package/src/wizard/source-detect.ts +87 -0
  146. package/src/wizard/status.ts +180 -0
  147. package/src/wizard/token-store.ts +73 -0
  148. package/src/wizard/tunnel-orchestrator.ts +197 -0
  149. package/src/wizard/verify.ts +236 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Managed-Caddy process lifecycle (GW-3). The Caddyfile's content is
3
+ * caddy.ts's job; this module owns everything with a heartbeat:
4
+ *
5
+ * start: ensureBinary (GW-2, cached after the first download) →
6
+ * generateCaddyfile (typed refusals) → write the Caddyfile as a
7
+ * `.part` sibling → `caddy validate --config <part> --adapter
8
+ * caddyfile` → only on a clean validate rename it to the real
9
+ * path → `caddy run --config <Caddyfile>`. The ticket's "validate
10
+ * before write" is honored as validate-before-PUBLISH: the final
11
+ * Caddyfile on disk only ever holds bytes Caddy accepted, and a
12
+ * failed validate leaves neither a start nor a published file.
13
+ *
14
+ * The Cloudflare API token travels in the child's ENVIRONMENT
15
+ * (`CF_API_TOKEN`, what the generated `{env.CF_API_TOKEN}` placeholder
16
+ * reads) — never in argv, which other processes on the machine can see.
17
+ *
18
+ * Idempotency: start with a live process refuses (`already-running`) —
19
+ * switching configuration stops first (design: 切换路时先停旧进程再启新的).
20
+ * stop is idempotent; an exited child clears its own slot so a later
21
+ * start works. The host wiring hands stop to ctx.effect, so the row's
22
+ * disposal kills Caddy too.
23
+ *
24
+ * The subprocess service is structural (the tests feed a recorder), and
25
+ * `fetch` is injectable for the ensureBinary step.
26
+ */
27
+ import { mkdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
28
+ import { ensureBinary, BinaryDownloadError } from './binary-io.js';
29
+ import { caddyDataDir, caddyfilePath, ingressDir } from './binary.js';
30
+ import { generateCaddyfile } from './caddy.js';
31
+ export function createCaddyManager(options) {
32
+ const { subprocess, dshHome } = options;
33
+ const maxLogBytes = options.maxLogBytes ?? 64 * 1024;
34
+ let handle = null;
35
+ let caddyfileArg;
36
+ let startedOnce = false;
37
+ // GW-fix-2/L3: a WHITELIST, not the host's full environment — the host
38
+ // process carries the relay secret and DSH's own credentials in env, and
39
+ // a child that only needs to reach the internet and the local disk has
40
+ // no business inheriting them. Include Windows system/profile variables so
41
+ // Go's TLS root-cert store and winsock initialize cleanly on Windows.
42
+ const childEnv = (cfToken) => {
43
+ const env = {};
44
+ for (const key of [
45
+ 'PATH', 'HOME', 'TMPDIR', 'LANG', 'TZ',
46
+ 'SystemRoot', 'SYSTEMROOT', 'windir', 'WINDIR',
47
+ 'USERPROFILE', 'APPDATA', 'LOCALAPPDATA', 'PROGRAMDATA', 'TEMP', 'TMP',
48
+ ]) {
49
+ if (process.env[key] !== undefined)
50
+ env[key] = process.env[key];
51
+ }
52
+ // The one secret this child needs, by env only (the Caddyfile reads
53
+ // {env.CF_API_TOKEN}) — never argv.
54
+ env.CF_API_TOKEN = cfToken;
55
+ return env;
56
+ };
57
+ const spawnChild = (argv, env) => subprocess.spawn({
58
+ argv,
59
+ cwd: dshHome,
60
+ env,
61
+ stdio: {
62
+ stdin: 'ignore',
63
+ stdout: { maxBytes: maxLogBytes },
64
+ stderr: { maxBytes: maxLogBytes },
65
+ },
66
+ graceMs: 3000,
67
+ });
68
+ return {
69
+ async start(input) {
70
+ if (handle !== null)
71
+ return { ok: false, error: 'already-running' };
72
+ const cfToken = typeof input.cfToken === 'string' && input.cfToken.trim() !== '' ? input.cfToken : undefined;
73
+ if (cfToken === undefined)
74
+ return { ok: false, error: 'missing-token' };
75
+ const generated = generateCaddyfile({ ...input, storageDir: caddyDataDir(dshHome) });
76
+ if (!generated.ok)
77
+ return { ok: false, error: 'bad-config', detail: generated.error };
78
+ let binaryPath;
79
+ try {
80
+ binaryPath = (await ensureBinary(dshHome, 'caddy', {
81
+ fetchImpl: options.fetchImpl,
82
+ platform: options.platform,
83
+ onProgress: options.onProgress,
84
+ })).path;
85
+ }
86
+ catch (error) {
87
+ if (error instanceof BinaryDownloadError) {
88
+ return { ok: false, error: 'download-failed', detail: error.message, installUrl: error.installUrl };
89
+ }
90
+ return { ok: false, error: 'download-failed', detail: String(error?.message ?? error) };
91
+ }
92
+ // Write → validate → publish: the `.part` file carries the exact
93
+ // bytes under validation, so what lands at the real path is exactly
94
+ // what Caddy accepted.
95
+ const finalPath = caddyfilePath(dshHome);
96
+ const partPath = `${finalPath}.part`;
97
+ try {
98
+ mkdirSync(ingressDir(dshHome), { recursive: true });
99
+ writeFileSync(partPath, generated.caddyfile, { mode: 0o600 });
100
+ const validate = spawnChild([binaryPath, 'validate', '--config', partPath, '--adapter', 'caddyfile'], childEnv(cfToken));
101
+ const outcome = await validate.done;
102
+ if ((outcome.exitCode ?? 1) !== 0) {
103
+ rmSync(partPath, { force: true });
104
+ return { ok: false, error: 'validate-failed' };
105
+ }
106
+ renameSync(partPath, finalPath);
107
+ }
108
+ catch (error) {
109
+ rmSync(partPath, { force: true });
110
+ return { ok: false, error: 'spawn-failed', detail: String(error?.message ?? error) };
111
+ }
112
+ const env = childEnv(cfToken);
113
+ try {
114
+ const child = spawnChild([binaryPath, 'run', '--config', finalPath], env);
115
+ handle = child;
116
+ caddyfileArg = finalPath;
117
+ startedOnce = true;
118
+ // An exited child clears its own slot: isRunning() goes honest and
119
+ // a later start() works without a manual stop.
120
+ void child.done.then(() => {
121
+ if (handle === child) {
122
+ handle = null;
123
+ caddyfileArg = undefined;
124
+ }
125
+ }).catch(() => {
126
+ if (handle === child) {
127
+ handle = null;
128
+ caddyfileArg = undefined;
129
+ }
130
+ });
131
+ return { ok: true, binaryPath, caddyfile: finalPath, alreadyRunning: false };
132
+ }
133
+ catch (error) {
134
+ handle = null;
135
+ caddyfileArg = undefined;
136
+ return { ok: false, error: 'spawn-failed', detail: String(error?.message ?? error) };
137
+ }
138
+ },
139
+ stop() {
140
+ if (handle === null)
141
+ return;
142
+ const child = handle;
143
+ handle = null;
144
+ caddyfileArg = undefined;
145
+ try {
146
+ void child.terminate();
147
+ }
148
+ catch {
149
+ // A dead child's terminate is a no-op or a throw — either way done.
150
+ }
151
+ },
152
+ isRunning: () => handle !== null,
153
+ runningCaddyfile: () => caddyfileArg,
154
+ /** Whether a start has EVER succeeded in this row's lifetime — the
155
+ * three-state status needs it to tell 已退出 (degraded) from 未启动. */
156
+ hasStarted: () => startedOnce,
157
+ };
158
+ }
159
+ /** Re-exported so the wiring (and later the routes) need one import for
160
+ * the whole ingress-directory layout. */
161
+ export { caddyDataDir, caddyfilePath, ingressDir };
162
+ //# sourceMappingURL=caddy-process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caddy-process.js","sourceRoot":"","sources":["../../src/wizard/caddy-process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Caddyfile generation (GW-3), pure logic — every byte the managed Caddy
3
+ * will run comes out of {@link generateCaddyfile}, so the validation and
4
+ * the injection guards live HERE and the process half (caddy-process.ts)
5
+ * only writes, validates and spawns what this answered.
6
+ *
7
+ * Shape (design.md 路 A), with one deliberate correction to the design
8
+ * sketch: the global DNS-01 option in a real Caddyfile is `acme_dns
9
+ * <provider> <token-placeholder>` — the sketch's bare `dns cloudflare`
10
+ * line is site-block `tls` syntax and is not valid as a global option.
11
+ * The DNS-01 challenge itself is the point: the certificate is ordered
12
+ * through Cloudflare's API (the token rides the `CF_API_TOKEN`
13
+ * environment variable, never the file), so neither 80 nor 443 needs to
14
+ * be reachable from outside for issuance; only the HTTPS service port
15
+ * must be.
16
+ *
17
+ * Injection posture: every interpolated value passes a charset gate —
18
+ * the domain through config.ts's `hostOf` (the exact validator the row
19
+ * layer applies), the storage path free of control characters, quotes and
20
+ * braces (Caddyfile's block delimiters), the env-var name as a legal
21
+ * POSIX name. Nothing else from the outside world reaches the output.
22
+ */
23
+ import { hostOf } from '../config.js';
24
+ /** The default environment variable the Cloudflare token rides in on. */
25
+ export const CF_TOKEN_ENV_DEFAULT = 'CF_API_TOKEN';
26
+ /** Integers in [1, 65535] only. */
27
+ function portOf(value) {
28
+ return typeof value === 'number' && Number.isInteger(value) && value >= 1 && value <= 65535 ? value : undefined;
29
+ }
30
+ /** Storage paths go into the Caddyfile quoted; anything that could break
31
+ * out of the quoting or forge a block delimiter is refused — control
32
+ * characters (newlines foremost), quotes, braces. */
33
+ function storageDirOf(value) {
34
+ if (typeof value !== 'string' || value === '')
35
+ return undefined;
36
+ // eslint-disable-next-line no-control-regex
37
+ if (/[\u0000-\u001f\u007f"{}]/.test(value))
38
+ return undefined;
39
+ return value;
40
+ }
41
+ /** Env names: POSIX uppercase convention, strictly. */
42
+ const ENV_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
43
+ /**
44
+ * Build the complete Caddyfile for the managed Caddy. Refuses (typed)
45
+ * instead of throwing so the caller can map each refusal to a form error.
46
+ */
47
+ export function generateCaddyfile(input) {
48
+ const domain = hostOf(input.domain);
49
+ if (domain === undefined)
50
+ return { ok: false, error: 'bad-domain' };
51
+ const httpsPort = portOf(input.httpsPort);
52
+ if (httpsPort === undefined)
53
+ return { ok: false, error: 'bad-https-port' };
54
+ const gatewayPort = portOf(input.gatewayPort);
55
+ if (gatewayPort === undefined)
56
+ return { ok: false, error: 'bad-gateway-port' };
57
+ const storageDir = storageDirOf(input.storageDir);
58
+ if (storageDir === undefined)
59
+ return { ok: false, error: 'bad-storage' };
60
+ const tokenEnv = typeof input.cfTokenEnvName === 'string' && ENV_NAME_PATTERN.test(input.cfTokenEnvName)
61
+ ? input.cfTokenEnvName
62
+ : CF_TOKEN_ENV_DEFAULT;
63
+ if (input.cfTokenEnvName !== undefined && input.cfTokenEnvName !== '' && tokenEnv !== input.cfTokenEnvName) {
64
+ return { ok: false, error: 'bad-token-env' };
65
+ }
66
+ const caddyfile = [
67
+ '{',
68
+ `\tacme_dns cloudflare {env.${tokenEnv}}`,
69
+ `\tstorage file_system "${storageDir}"`,
70
+ '\tadmin off',
71
+ '}',
72
+ '',
73
+ `https://${domain}:${httpsPort} {`,
74
+ `\treverse_proxy 127.0.0.1:${gatewayPort}`,
75
+ '}',
76
+ '',
77
+ ].join('\n');
78
+ return { ok: true, caddyfile, domain, httpsPort, gatewayPort };
79
+ }
80
+ //# sourceMappingURL=caddy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caddy.js","sourceRoot":"","sources":["../../src/wizard/caddy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC,yEAAyE;AACzE,MAAM,CAAC,MAAM,oBAAoB,GAAG,cAAc,CAAA;AAoBlD,mCAAmC;AACnC,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AACjH,CAAC;AAED;;qDAEqD;AACrD,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,SAAS,CAAA;IAC/D,4CAA4C;IAC5C,IAAI,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5D,OAAO,KAAK,CAAA;AACd,CAAC;AAED,uDAAuD;AACvD,MAAM,gBAAgB,GAAG,oBAAoB,CAAA;AAE7C;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAqB;IACrD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACnC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,CAAA;IACnE,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACzC,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAA;IAC1E,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;IAC7C,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAA;IAC9E,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IACjD,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA;IACxE,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC,cAAc,KAAK,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC;QACtG,CAAC,CAAC,KAAK,CAAC,cAAc;QACtB,CAAC,CAAC,oBAAoB,CAAA;IACxB,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS,IAAI,KAAK,CAAC,cAAc,KAAK,EAAE,IAAI,QAAQ,KAAK,KAAK,CAAC,cAAc,EAAE,CAAC;QAC3G,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,CAAA;IAC9C,CAAC;IACD,MAAM,SAAS,GAAG;QAChB,GAAG;QACH,8BAA8B,QAAQ,GAAG;QACzC,0BAA0B,UAAU,GAAG;QACvC,aAAa;QACb,GAAG;QACH,EAAE;QACF,WAAW,MAAM,IAAI,SAAS,IAAI;QAClC,6BAA6B,WAAW,EAAE;QAC1C,GAAG;QACH,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACZ,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,CAAA;AAChE,CAAC"}
@@ -0,0 +1,334 @@
1
+ /**
2
+ * Cloudflare API v4 client (GW-7) — the whole tunnel path (路 C) talks to
3
+ * this module: account lookup, zone lookup, tunnel create/list, ingress
4
+ * configuration, the CNAME that points the public hostname at the tunnel,
5
+ * and the composed "is this domain on Cloudflare already" check.
6
+ *
7
+ * Transport rules the ticket pins down:
8
+ *
9
+ * - Plain `fetch`, no third-party dependency; the token rides the
10
+ * `Authorization: Bearer` header and is NEVER logged, echoed into an
11
+ * error, or written into any returned structure.
12
+ * - Every failure is a typed {@link CloudflareError} whose `code` the UI
13
+ * classifies on (`cf-auth-failed`, `cf-zone-not-found`,
14
+ * `cf-tunnel-conflict`, `cf-dns-conflict`, `cf-network`, and the
15
+ * catch-all `cf-api`). Raw response bodies never pass through — the
16
+ * message carries only the API's short error summary, truncated,
17
+ * which is what the design's 「兜底显示原始错误码」 wants.
18
+ * - The client is stateless: a factory binds the token (plus injectable
19
+ * `fetchImpl` / `baseUrl` for the mock-server tests); every method
20
+ * takes the ids it needs. The module-level `cf*` wrappers are one-call
21
+ * conveniences over the factory.
22
+ *
23
+ * Conflict classification matches the well-known numeric codes where they
24
+ * exist and falls back to the API's error text (`already exists`
25
+ * wordings), so an upstream renumber degrades to the generic envelope
26
+ * instead of a wrong verdict.
27
+ */
28
+ import { hostOf } from '../config.js';
29
+ export const CLOUDFLARE_API_BASE = 'https://api.cloudflare.com/client/v4';
30
+ /**
31
+ * One refused API call. Fields are assigned in the constructor body (the
32
+ * check scripts' strip-only type stripping rejects parameter properties).
33
+ */
34
+ export class CloudflareError extends Error {
35
+ code;
36
+ status;
37
+ /** The API's own numeric error code(s), when the envelope carried any. */
38
+ apiCodes;
39
+ constructor(code, message, status = null, apiCodes = [], options) {
40
+ super(message, options);
41
+ this.name = 'CloudflareError';
42
+ this.code = code;
43
+ this.status = status;
44
+ this.apiCodes = apiCodes;
45
+ }
46
+ }
47
+ /** Tunnel name conflict: Cloudflare rejects duplicate names (documented
48
+ * code 10405; deployments also word it in the message text). The text
49
+ * fallback names its SUBJECT — "already exists" alone matches the DNS
50
+ * wording just as well, so the two classifiers must stay scoped. */
51
+ function isTunnelConflict(apiCodes, text) {
52
+ return apiCodes.includes(10405) || (/tunnel/i.test(text) && /already exists/i.test(text));
53
+ }
54
+ /** DNS record conflict: the documented codes are 81053 / 81057. */
55
+ function isDnsConflict(apiCodes, text) {
56
+ return apiCodes.includes(81053) || apiCodes.includes(81057) || (/record/i.test(text) && /already exists/i.test(text));
57
+ }
58
+ /** The API's error summary: message strings only, joined, truncated —
59
+ * never the raw body, never the request's token. */
60
+ function summarize(errors, fallback) {
61
+ const codes = [];
62
+ const parts = [];
63
+ for (const entry of errors) {
64
+ if (entry !== null && typeof entry === 'object') {
65
+ const record = entry;
66
+ if (typeof record.code === 'number')
67
+ codes.push(record.code);
68
+ if (typeof record.message === 'string' && record.message !== '')
69
+ parts.push(record.message);
70
+ }
71
+ }
72
+ return { text: (parts.join('; ') || fallback).slice(0, 200), codes };
73
+ }
74
+ /** Tunnel names we mint: short, safe in a URL path. */
75
+ const TUNNEL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]{0,62}$/;
76
+ /** A `service:` URL inside a tunnel ingress: http(s) to host[:port]. */
77
+ const SERVICE_URL_PATTERN = /^https?:\/\/[A-Za-z0-9.[\]:-]+$/;
78
+ /** Path segments are ids we received from the API or names we minted —
79
+ * encoded anyway, so a hostile value cannot change the request shape. */
80
+ const seg = (value) => encodeURIComponent(value);
81
+ /** Page-size + page ceiling for the two list endpoints: 5 pages of 50 is
82
+ * 250 accounts/tunnels — far past anything a wizard user has, and the hard
83
+ * stop means a broken pagination answer can never loop forever. */
84
+ const PAGE_SIZE = 50;
85
+ const PAGE_LIMIT = 5;
86
+ /**
87
+ * The ancestor chain of one hostname, itself first: `dsh.example.com` →
88
+ * `dsh.example.com`, `example.com`, `com`. Cloudflare zones are apex
89
+ * domains, so a wizard lookup for `dsh.example.com` must find the zone
90
+ * named `example.com` (GW-fix-2/H1) — exact-name matching walks this chain
91
+ * level by level.
92
+ */
93
+ export function ancestorHosts(domain) {
94
+ const labels = domain.split('.');
95
+ const chain = [];
96
+ for (let index = 0; index < labels.length; index++)
97
+ chain.push(labels.slice(index).join('.'));
98
+ return chain;
99
+ }
100
+ export function createCloudflareApi(options) {
101
+ const baseUrl = options.baseUrl ?? CLOUDFLARE_API_BASE;
102
+ const fetchImpl = options.fetchImpl ?? fetch;
103
+ const token = options.token;
104
+ if (typeof token !== 'string' || token === '') {
105
+ throw new CloudflareError('cf-auth-failed', 'no Cloudflare API token');
106
+ }
107
+ /** One authenticated v4 call: Bearer header, envelope unwrapping, typed
108
+ * failures. The token never appears in any error path. Returns the
109
+ * result PLUS the pagination envelope (the list endpoints page). */
110
+ const call = async (method, path, body) => {
111
+ let response;
112
+ try {
113
+ response = await fetchImpl(`${baseUrl}${path}`, {
114
+ method,
115
+ headers: {
116
+ Authorization: `Bearer ${token}`,
117
+ ...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
118
+ },
119
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
120
+ signal: AbortSignal.timeout(15_000),
121
+ });
122
+ }
123
+ catch (cause) {
124
+ throw new CloudflareError('cf-network', `the Cloudflare API call failed before an answer arrived (${path})`, null, [], { cause });
125
+ }
126
+ let envelope;
127
+ try {
128
+ envelope = await response.json();
129
+ }
130
+ catch {
131
+ throw new CloudflareError('cf-network', `the Cloudflare API answered unparsable JSON (${path})`, response.status);
132
+ }
133
+ const errors = Array.isArray(envelope?.errors) ? envelope.errors : [];
134
+ const summary = summarize(errors, `HTTP ${response.status}`);
135
+ if (response.status === 401 || response.status === 403) {
136
+ throw new CloudflareError('cf-auth-failed', summary.text, response.status, summary.codes);
137
+ }
138
+ if (envelope?.success !== true) {
139
+ let code = 'cf-api';
140
+ if (isTunnelConflict(summary.codes, summary.text))
141
+ code = 'cf-tunnel-conflict';
142
+ else if (isDnsConflict(summary.codes, summary.text))
143
+ code = 'cf-dns-conflict';
144
+ throw new CloudflareError(code, summary.text, response.status, summary.codes);
145
+ }
146
+ // A missing or junk total_pages reads as a single page — old mocks and
147
+ // tolerant callers keep working, pagination simply never continues.
148
+ const totalPages = typeof envelope.result_info?.total_pages === 'number' && envelope.result_info.total_pages >= 1
149
+ ? envelope.result_info.total_pages
150
+ : 1;
151
+ return { result: envelope.result, totalPages };
152
+ };
153
+ const requireString = (value, what) => {
154
+ if (typeof value !== 'string' || value === '')
155
+ throw new CloudflareError('cf-api', `the Cloudflare API answered without ${what}`);
156
+ return value;
157
+ };
158
+ const requireId = (value, what) => {
159
+ if (typeof value !== 'string' || value === '')
160
+ throw new CloudflareError('cf-api', `${what} is required`);
161
+ return value;
162
+ };
163
+ return {
164
+ async listAccounts() {
165
+ const accounts = [];
166
+ for (let page = 1; page <= PAGE_LIMIT; page++) {
167
+ const { result, totalPages } = await call('GET', `/accounts?per_page=${PAGE_SIZE}&page=${page}`);
168
+ if (!Array.isArray(result))
169
+ throw new CloudflareError('cf-api', 'the accounts answer had no list');
170
+ accounts.push(...result.map((account) => requireString(account?.id, 'an account id')));
171
+ if (page >= totalPages || result.length === 0)
172
+ break;
173
+ }
174
+ return accounts;
175
+ },
176
+ async getZone(accountId, domain) {
177
+ const host = hostOf(domain);
178
+ if (host === undefined)
179
+ throw new CloudflareError('cf-api', 'the domain is not a bare hostname');
180
+ const id = requireId(accountId, 'the account id');
181
+ const { result } = await call('GET', `/zones?name=${seg(host)}&account.id=${seg(id)}`);
182
+ // Exact-match on the zone NAME client-side: the query filter is
183
+ // advisory, the verdict is ours.
184
+ const zones = Array.isArray(result) ? result : [];
185
+ const zone = zones.find((candidate) => candidate?.name === host);
186
+ if (zone === undefined)
187
+ throw new CloudflareError('cf-zone-not-found', `no Cloudflare zone named ${host}`);
188
+ return requireString(zone.id, 'a zone id');
189
+ },
190
+ async createTunnel(accountId, name) {
191
+ const id = requireId(accountId, 'the account id');
192
+ if (typeof name !== 'string' || !TUNNEL_NAME_PATTERN.test(name)) {
193
+ throw new CloudflareError('cf-api', 'the tunnel name is not a safe label');
194
+ }
195
+ // config_src cloudflare makes the create response carry the connector
196
+ // token directly — the second round-trip (GET .../token) is spared.
197
+ const { result } = await call('POST', `/accounts/${seg(id)}/cfd_tunnel`, { name, config_src: 'cloudflare' });
198
+ const record = result;
199
+ return { id: requireString(record?.id, 'a tunnel id'), token: requireString(record?.token, 'a tunnel token') };
200
+ },
201
+ async getTunnelToken(accountId, tunnelId) {
202
+ const id = requireId(accountId, 'the account id');
203
+ const tunnel = requireId(tunnelId, 'the tunnel id');
204
+ const { result } = await call('GET', `/accounts/${seg(id)}/cfd_tunnel/${seg(tunnel)}/token`);
205
+ const tokenValue = typeof result === 'string'
206
+ ? result
207
+ : result?.token;
208
+ return requireString(tokenValue, 'a tunnel token');
209
+ },
210
+ async listTunnels(accountId) {
211
+ const id = requireId(accountId, 'the account id');
212
+ const tunnels = [];
213
+ for (let page = 1; page <= PAGE_LIMIT; page++) {
214
+ const { result, totalPages } = await call('GET', `/accounts/${seg(id)}/cfd_tunnel?is_deleted=false&per_page=${PAGE_SIZE}&page=${page}`);
215
+ if (!Array.isArray(result))
216
+ throw new CloudflareError('cf-api', 'the tunnels answer had no list');
217
+ tunnels.push(...result.map((tunnel) => {
218
+ const record = tunnel;
219
+ return {
220
+ id: requireString(record?.id, 'a tunnel id'),
221
+ name: typeof record?.name === 'string' ? record.name : '',
222
+ status: typeof record?.status === 'string' ? record.status : '',
223
+ };
224
+ }));
225
+ if (page >= totalPages || result.length === 0)
226
+ break;
227
+ }
228
+ return tunnels;
229
+ },
230
+ async configureIngress(accountId, tunnelId, domain, serviceUrl) {
231
+ const accountIdSafe = requireId(accountId, 'the account id');
232
+ const tunnelIdSafe = requireId(tunnelId, 'the tunnel id');
233
+ const host = hostOf(domain);
234
+ if (host === undefined)
235
+ throw new CloudflareError('cf-api', 'the domain is not a bare hostname');
236
+ if (typeof serviceUrl !== 'string' || !SERVICE_URL_PATTERN.test(serviceUrl)) {
237
+ throw new CloudflareError('cf-api', 'the service URL is not a plain http(s) origin');
238
+ }
239
+ await call('PUT', `/accounts/${seg(accountIdSafe)}/cfd_tunnel/${seg(tunnelIdSafe)}/configurations`, {
240
+ config: {
241
+ ingress: [
242
+ { hostname: host, service: serviceUrl },
243
+ { service: 'http_status:404' },
244
+ ],
245
+ },
246
+ });
247
+ },
248
+ async listDnsRecords(zoneId, name) {
249
+ const zone = requireId(zoneId, 'the zone id');
250
+ const recordName = hostOf(name);
251
+ if (recordName === undefined)
252
+ throw new CloudflareError('cf-api', 'the record name is not a bare hostname');
253
+ const { result } = await call('GET', `/zones/${seg(zone)}/dns_records?type=CNAME&name=${seg(recordName)}&per_page=${50}&page=${1}`);
254
+ if (!Array.isArray(result))
255
+ throw new CloudflareError('cf-api', 'the DNS records answer had no list');
256
+ return result.map((record) => {
257
+ const entry = record;
258
+ return {
259
+ id: requireString(entry?.id, 'a DNS record id'),
260
+ name: typeof entry?.name === 'string' ? entry.name : '',
261
+ content: typeof entry?.content === 'string' ? entry.content : '',
262
+ };
263
+ });
264
+ },
265
+ async createDnsRecord(zoneId, subdomain, target) {
266
+ const zone = requireId(zoneId, 'the zone id');
267
+ const name = hostOf(subdomain);
268
+ if (name === undefined)
269
+ throw new CloudflareError('cf-api', 'the record name is not a bare hostname');
270
+ if (typeof target !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9-]{0,62}\.cfargotunnel\.com$/.test(target)) {
271
+ throw new CloudflareError('cf-api', 'the CNAME target is not a tunnel hostname');
272
+ }
273
+ const { result } = await call('POST', `/zones/${seg(zone)}/dns_records`, {
274
+ type: 'CNAME',
275
+ name,
276
+ content: target,
277
+ proxied: true,
278
+ ttl: 1,
279
+ });
280
+ return requireString(result?.id, 'a DNS record id');
281
+ },
282
+ async checkDomainOnCloudflare(domain) {
283
+ const host = hostOf(domain);
284
+ if (host === undefined)
285
+ throw new CloudflareError('cf-api', 'the domain is not a bare hostname');
286
+ // Zones are APEX domains: the user's `dsh.example.com` lives in the
287
+ // zone named `example.com`, so the exact-name lookup walks the
288
+ // ancestor chain (GW-fix-2/H1). First hit wins.
289
+ const chain = ancestorHosts(host);
290
+ for (const accountId of await this.listAccounts()) {
291
+ for (const candidate of chain) {
292
+ try {
293
+ const zoneId = await this.getZone(accountId, candidate);
294
+ return { onCloudflare: true, zoneId, accountId };
295
+ }
296
+ catch (error) {
297
+ if (error instanceof CloudflareError && error.code === 'cf-zone-not-found')
298
+ continue;
299
+ throw error;
300
+ }
301
+ }
302
+ }
303
+ return { onCloudflare: false, zoneId: null, accountId: null };
304
+ },
305
+ };
306
+ }
307
+ export function cfListAccounts(token, opts) {
308
+ return createCloudflareApi({ token, ...opts }).listAccounts();
309
+ }
310
+ export function cfGetZone(token, accountId, domain, opts) {
311
+ return createCloudflareApi({ token, ...opts }).getZone(accountId, domain);
312
+ }
313
+ export function cfCreateTunnel(token, accountId, name, opts) {
314
+ return createCloudflareApi({ token, ...opts }).createTunnel(accountId, name);
315
+ }
316
+ export function cfListTunnels(token, accountId, opts) {
317
+ return createCloudflareApi({ token, ...opts }).listTunnels(accountId);
318
+ }
319
+ export function cfGetTunnelToken(token, accountId, tunnelId, opts) {
320
+ return createCloudflareApi({ token, ...opts }).getTunnelToken(accountId, tunnelId);
321
+ }
322
+ export function cfConfigureIngress(token, accountId, tunnelId, domain, serviceUrl, opts) {
323
+ return createCloudflareApi({ token, ...opts }).configureIngress(accountId, tunnelId, domain, serviceUrl);
324
+ }
325
+ export function cfCreateDnsRecord(token, zoneId, subdomain, target, opts) {
326
+ return createCloudflareApi({ token, ...opts }).createDnsRecord(zoneId, subdomain, target);
327
+ }
328
+ export function cfListDnsRecords(token, zoneId, name, opts) {
329
+ return createCloudflareApi({ token, ...opts }).listDnsRecords(zoneId, name);
330
+ }
331
+ export function cfCheckDomainOnCloudflare(token, domain, opts) {
332
+ return createCloudflareApi({ token, ...opts }).checkDomainOnCloudflare(domain);
333
+ }
334
+ //# sourceMappingURL=cloudflare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../src/wizard/cloudflare.ts"],"names":[],"mappings":"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"}