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,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wizard route skeleton (GW-4, extended by GW-5): every
|
|
3
|
+
* `/_dsh/zen-remote/admin/wizard/*` route the gateway wizard serves, with
|
|
4
|
+
* the security walls applied to ALL of them, in this order:
|
|
5
|
+
*
|
|
6
|
+
* 1. `connection.admit` — webServer-registered routes skip DSH's `/api`
|
|
7
|
+
* authentication, so each request asks the connection service first;
|
|
8
|
+
* its rejection is relayed verbatim (401/403).
|
|
9
|
+
* 2. The gateway marker. A request wearing `x-zen-remote-via` came
|
|
10
|
+
* THROUGH the gateway from a remote device. Unlike the admin status
|
|
11
|
+
* route there is no read-only exception: the wizard is a server-owner
|
|
12
|
+
* surface, and a remote device gets a flat 403 `via-gateway` — GET
|
|
13
|
+
* included. The marker wall runs BEFORE same-origin because a
|
|
14
|
+
* forwarded request presents as same-origin (the gateway rewrites
|
|
15
|
+
* Origin/Host onto the upstream origin); the marker is the only thing
|
|
16
|
+
* identifying it.
|
|
17
|
+
* 3. `sameOriginPost` for every POST.
|
|
18
|
+
*
|
|
19
|
+
* Real routes: `GET /wizard/status` (the sanitized projection from
|
|
20
|
+
* status.ts — reads only, never mutates) and, since GW-5, `POST
|
|
21
|
+
* /wizard/trust-proxy` (merge one detected proxy IP into the persisted
|
|
22
|
+
* `trustedProxies` list). Every other POST under the prefix is a 501
|
|
23
|
+
* placeholder; later tasks fill those in. GET /status answers from a
|
|
24
|
+
* per-request config resolution and the injected runtime probe; a throwing
|
|
25
|
+
* runtime probe degrades to "nothing configured" instead of failing the
|
|
26
|
+
* read.
|
|
27
|
+
*
|
|
28
|
+
* The handler is built by a factory so the tests drive it over a real
|
|
29
|
+
* socket with mock admit/config (same seam as the admin routes).
|
|
30
|
+
*/
|
|
31
|
+
import { responseJson, sameOriginPost } from '../http.js';
|
|
32
|
+
import { hostOf } from '../config.js';
|
|
33
|
+
import { normalizeTrustedIp, mergeProxyList, parseProxyList } from './source-detect.js';
|
|
34
|
+
import { projectWizardStatus } from './status.js';
|
|
35
|
+
/** Prefix all wizard routes live under (one webServer prefix registration;
|
|
36
|
+
* the host web server matches longest-prefix-first, so these win over the
|
|
37
|
+
* plain admin prefix registration). */
|
|
38
|
+
export const WIZARD_ROUTE_PREFIX = '/_dsh/zen-remote/admin/wizard';
|
|
39
|
+
/** GET: the sanitized wizard status projection. */
|
|
40
|
+
export const WIZARD_STATUS_ROUTE = `${WIZARD_ROUTE_PREFIX}/status`;
|
|
41
|
+
/** POST `{ip}`: merge one detected reverse-proxy IP into the persisted
|
|
42
|
+
* trustedProxies (GW-5). */
|
|
43
|
+
export const WIZARD_TRUST_PROXY_ROUTE = `${WIZARD_ROUTE_PREFIX}/trust-proxy`;
|
|
44
|
+
/** POST `{publicUrl}`: start the final verification — mint the web pairing
|
|
45
|
+
* code (GW-9). GET: poll its status. */
|
|
46
|
+
export const WIZARD_VERIFY_ROUTE = `${WIZARD_ROUTE_PREFIX}/verify`;
|
|
47
|
+
/** POST `{token}`: store the Cloudflare API token 0600 on this machine;
|
|
48
|
+
* DELETE: stop the consumers and remove it (GW-13a). */
|
|
49
|
+
export const WIZARD_CF_TOKEN_ROUTE = `${WIZARD_ROUTE_PREFIX}/cf-token`;
|
|
50
|
+
/** POST `{token}`: store the cloudflared connector token (GW-13a). */
|
|
51
|
+
export const WIZARD_TUNNEL_TOKEN_ROUTE = `${WIZARD_ROUTE_PREFIX}/tunnel-token`;
|
|
52
|
+
/** POST: start the managed Caddy from the saved row config; POST (stop)
|
|
53
|
+
* terminates it; GET answers its three-state (GW-13a). */
|
|
54
|
+
export const WIZARD_CADDY_START_ROUTE = `${WIZARD_ROUTE_PREFIX}/caddy/start`;
|
|
55
|
+
export const WIZARD_CADDY_STOP_ROUTE = `${WIZARD_ROUTE_PREFIX}/caddy/stop`;
|
|
56
|
+
export const WIZARD_CADDY_STATUS_ROUTE = `${WIZARD_ROUTE_PREFIX}/caddy/status`;
|
|
57
|
+
/** POST `{domain}`: the zone/account probe the migration card re-checks;
|
|
58
|
+
* POST `{domain, tunnelId?}`: the full tunnel setup; POST (stop) and GET
|
|
59
|
+
* (three-state) mirror the Caddy pair (GW-13b). */
|
|
60
|
+
export const WIZARD_TUNNEL_CHECK_ROUTE = `${WIZARD_ROUTE_PREFIX}/tunnel/check-domain`;
|
|
61
|
+
export const WIZARD_TUNNEL_SETUP_ROUTE = `${WIZARD_ROUTE_PREFIX}/tunnel/setup`;
|
|
62
|
+
export const WIZARD_TUNNEL_STOP_ROUTE = `${WIZARD_ROUTE_PREFIX}/tunnel/stop`;
|
|
63
|
+
export const WIZARD_TUNNEL_STATUS_ROUTE = `${WIZARD_ROUTE_PREFIX}/tunnel/status`;
|
|
64
|
+
/** Inbound body cap — the wizard forms send a few hundred bytes. */
|
|
65
|
+
const MAX_BODY_BYTES = 16 * 1024;
|
|
66
|
+
/**
|
|
67
|
+
* Read the whole body under the cap and demand one JSON object. An empty
|
|
68
|
+
* body counts as `{}`. Answers null for anything malformed — the route
|
|
69
|
+
* maps that to 400.
|
|
70
|
+
*/
|
|
71
|
+
async function readJsonObject(req) {
|
|
72
|
+
const declared = req.headers['content-length'];
|
|
73
|
+
const expected = declared === undefined ? undefined : Number(declared);
|
|
74
|
+
if (expected !== undefined && Number.isSafeInteger(expected) && expected > MAX_BODY_BYTES)
|
|
75
|
+
return null;
|
|
76
|
+
const chunks = [];
|
|
77
|
+
let size = 0;
|
|
78
|
+
for await (const chunk of req) {
|
|
79
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
80
|
+
size += bytes.length;
|
|
81
|
+
if (size > MAX_BODY_BYTES)
|
|
82
|
+
return null;
|
|
83
|
+
chunks.push(bytes);
|
|
84
|
+
}
|
|
85
|
+
const text = Buffer.concat(chunks).toString('utf8');
|
|
86
|
+
if (text.trim() === '')
|
|
87
|
+
return {};
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(text);
|
|
90
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
91
|
+
return null;
|
|
92
|
+
return parsed;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Build the wizard route handler for one plugin row. The returned handler
|
|
100
|
+
* owns the full response lifecycle of every request under
|
|
101
|
+
* {@link WIZARD_ROUTE_PREFIX} and never throws.
|
|
102
|
+
*/
|
|
103
|
+
export function createWizardHandler(options) {
|
|
104
|
+
return async (req, res) => {
|
|
105
|
+
// EVERYTHING rides inside the one guard (GW-fix-1/L1): a malformed
|
|
106
|
+
// req.url explodes in `new URL`, and a broken connection service can
|
|
107
|
+
// throw out of admit — neither may escape as an unhandled rejection,
|
|
108
|
+
// the handler owns the full response lifecycle, always.
|
|
109
|
+
try {
|
|
110
|
+
// Wall 1 first: DSH's own admission.
|
|
111
|
+
const admission = options.admit(req);
|
|
112
|
+
if ('rejection' in admission) {
|
|
113
|
+
responseJson(res, admission.rejection, {
|
|
114
|
+
ok: false,
|
|
115
|
+
error: { code: admission.rejection === 401 ? 'unauthorized' : 'forbidden' },
|
|
116
|
+
});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const url = new URL(req.url ?? '/', 'http://dsh.internal');
|
|
120
|
+
const route = url.pathname;
|
|
121
|
+
const method = req.method ?? 'GET';
|
|
122
|
+
// Wall 2, on EVERY wizard route before anything else: unlike
|
|
123
|
+
// admin/status there is no remote-readable exception — the wizard is
|
|
124
|
+
// a server-owner surface, GET included. This runs BEFORE same-origin
|
|
125
|
+
// because a forwarded request presents as same-origin (the gateway
|
|
126
|
+
// rewrites Origin/Host); the marker is the identifying signal.
|
|
127
|
+
if (req.headers['x-zen-remote-via'] !== undefined) {
|
|
128
|
+
responseJson(res, 403, {
|
|
129
|
+
ok: false,
|
|
130
|
+
error: { code: 'via-gateway', message: '配置引导只能在服务端本机操作' },
|
|
131
|
+
});
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (route === WIZARD_STATUS_ROUTE && method === 'GET') {
|
|
135
|
+
let runtime = {};
|
|
136
|
+
try {
|
|
137
|
+
runtime = (await options.getRuntime?.()) ?? {};
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// A broken runtime probe is "no information", never a failed read.
|
|
141
|
+
runtime = {};
|
|
142
|
+
}
|
|
143
|
+
responseJson(res, 200, projectWizardStatus(options.getConfig().values, runtime));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (route === WIZARD_TRUST_PROXY_ROUTE && method === 'POST') {
|
|
147
|
+
if (!sameOriginPost(req)) {
|
|
148
|
+
responseJson(res, 403, {
|
|
149
|
+
ok: false,
|
|
150
|
+
error: { code: 'origin-rejected', message: 'The request must originate from this DSH Web application' },
|
|
151
|
+
});
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const trust = options.trustProxy;
|
|
155
|
+
// A wiring that predates GW-5 has no persister: same answer as the
|
|
156
|
+
// other unwired verbs.
|
|
157
|
+
if (trust === undefined) {
|
|
158
|
+
responseJson(res, 501, { ok: false, error: { code: 'not-implemented', message: 'This wizard step is not wired up yet' } });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const body = await readJsonObject(req);
|
|
162
|
+
if (body === null) {
|
|
163
|
+
responseJson(res, 400, { ok: false, error: { code: 'bad-request', message: 'request body must be a JSON object' } });
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const ip = normalizeTrustedIp(body.ip);
|
|
167
|
+
if (ip === undefined) {
|
|
168
|
+
responseJson(res, 400, { ok: false, error: { code: 'bad-ip', message: 'ip must be an IPv4 or IPv6 literal' } });
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
let resolved;
|
|
172
|
+
try {
|
|
173
|
+
resolved = trust.resolved();
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
resolved = { value: '', source: 'default' };
|
|
177
|
+
}
|
|
178
|
+
// env / row values SHADOW the file layer the persister writes: a
|
|
179
|
+
// write there would silently do nothing. Refuse honestly instead.
|
|
180
|
+
if (resolved.source === 'env' || resolved.source === 'row') {
|
|
181
|
+
responseJson(res, 409, {
|
|
182
|
+
ok: false,
|
|
183
|
+
error: {
|
|
184
|
+
code: 'proxy-list-shadowed',
|
|
185
|
+
message: `trustedProxies 目前由${resolved.source === 'env' ? '环境变量' : '插件行配置'}提供,请先清掉那边的值再自动信任`,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (parseProxyList(resolved.value).includes(ip)) {
|
|
191
|
+
responseJson(res, 200, { ok: true, added: false });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const merged = mergeProxyList(resolved.value, ip);
|
|
195
|
+
// GW-fix-2/L2: the persister re-reads the persisted surface and
|
|
196
|
+
// refuses when it moved since `resolved()` — two 信任 clicks in
|
|
197
|
+
// quick succession must not lose either IP.
|
|
198
|
+
const landed = await trust.write(resolved.value, merged);
|
|
199
|
+
if (landed === false) {
|
|
200
|
+
responseJson(res, 409, {
|
|
201
|
+
ok: false,
|
|
202
|
+
error: { code: 'trust-list-changed', message: '信任列表刚被其他操作修改,请刷新后重试' },
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
responseJson(res, 200, { ok: true, added: true });
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (route === WIZARD_CF_TOKEN_ROUTE && (method === 'POST' || method === 'DELETE')) {
|
|
210
|
+
if (!sameOriginPost(req)) {
|
|
211
|
+
responseJson(res, 403, {
|
|
212
|
+
ok: false,
|
|
213
|
+
error: { code: 'origin-rejected', message: 'The request must originate from this DSH Web application' },
|
|
214
|
+
});
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const store = options.tokenStore;
|
|
218
|
+
if (store === undefined) {
|
|
219
|
+
responseJson(res, 501, { ok: false, error: { code: 'not-implemented', message: 'This wizard step is not wired up yet' } });
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (method === 'DELETE') {
|
|
223
|
+
store.deleteCfToken();
|
|
224
|
+
responseJson(res, 200, { ok: true, configured: false });
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const body = await readJsonObject(req);
|
|
228
|
+
const token = typeof body?.token === 'string' ? body.token.trim() : '';
|
|
229
|
+
if (token === '') {
|
|
230
|
+
responseJson(res, 400, { ok: false, error: { code: 'bad-token', message: 'token must be a non-empty string' } });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
store.writeCfToken(token);
|
|
234
|
+
responseJson(res, 200, { ok: true, configured: true });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (route === WIZARD_TUNNEL_TOKEN_ROUTE && method === 'POST') {
|
|
238
|
+
if (!sameOriginPost(req)) {
|
|
239
|
+
responseJson(res, 403, {
|
|
240
|
+
ok: false,
|
|
241
|
+
error: { code: 'origin-rejected', message: 'The request must originate from this DSH Web application' },
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const store = options.tokenStore;
|
|
246
|
+
if (store === undefined) {
|
|
247
|
+
responseJson(res, 501, { ok: false, error: { code: 'not-implemented', message: 'This wizard step is not wired up yet' } });
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const body = await readJsonObject(req);
|
|
251
|
+
const token = typeof body?.token === 'string' ? body.token.trim() : '';
|
|
252
|
+
if (token === '') {
|
|
253
|
+
responseJson(res, 400, { ok: false, error: { code: 'bad-token', message: 'token must be a non-empty string' } });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
store.writeTunnelToken(token);
|
|
257
|
+
responseJson(res, 200, { ok: true, configured: true });
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (route.startsWith(`${WIZARD_ROUTE_PREFIX}/caddy/`)) {
|
|
261
|
+
// Walls first, then the method shape, then (and only then) the
|
|
262
|
+
// wiring check — a 501 must never answer before the walls, and a
|
|
263
|
+
// wrong method on a wired route must still read 405.
|
|
264
|
+
if (method === 'POST' && !sameOriginPost(req)) {
|
|
265
|
+
responseJson(res, 403, {
|
|
266
|
+
ok: false,
|
|
267
|
+
error: { code: 'origin-rejected', message: 'The request must originate from this DSH Web application' },
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (method !== 'GET' && method !== 'POST') {
|
|
272
|
+
res.setHeader('Allow', 'GET, POST');
|
|
273
|
+
responseJson(res, 405, { ok: false, error: { code: 'method-not-allowed', message: 'Use GET or POST' } });
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const caddy = options.caddy;
|
|
277
|
+
if (caddy === undefined) {
|
|
278
|
+
responseJson(res, 501, { ok: false, error: { code: 'not-implemented', message: 'This wizard step is not wired up yet' } });
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (route === WIZARD_CADDY_STATUS_ROUTE && method === 'GET') {
|
|
282
|
+
responseJson(res, 200, { ok: true, state: caddy.status() });
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (route === WIZARD_CADDY_STOP_ROUTE && method !== 'POST') {
|
|
286
|
+
// GW-fix-4/H2: a GET must never stop a process, even by accident.
|
|
287
|
+
res.setHeader('Allow', 'GET, POST');
|
|
288
|
+
responseJson(res, 405, { ok: false, error: { code: 'method-not-allowed', message: 'Use POST to stop' } });
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (route === WIZARD_CADDY_STOP_ROUTE) {
|
|
292
|
+
caddy.stop();
|
|
293
|
+
responseJson(res, 200, { ok: true, state: caddy.status() });
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (route === WIZARD_CADDY_START_ROUTE && method === 'POST') {
|
|
297
|
+
const body = await readJsonObject(req);
|
|
298
|
+
const values = options.getConfig().values;
|
|
299
|
+
const rawDomain = typeof body?.domain === 'string' && body.domain.trim() !== ''
|
|
300
|
+
? body.domain.trim()
|
|
301
|
+
: values.ingressDomain;
|
|
302
|
+
const domain = hostOf(rawDomain);
|
|
303
|
+
if (domain === undefined) {
|
|
304
|
+
responseJson(res, 400, { ok: false, error: { code: 'no-domain', message: '保存一个有效的公开域名后再启动' } });
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const rawPort = typeof body?.httpsPort === 'number' && Number.isSafeInteger(body.httpsPort) && body.httpsPort >= 1 && body.httpsPort <= 65535
|
|
308
|
+
? body.httpsPort
|
|
309
|
+
: values.ingressHttpsPort;
|
|
310
|
+
let runtime = {};
|
|
311
|
+
try {
|
|
312
|
+
runtime = (await options.getRuntime?.()) ?? {};
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
runtime = {};
|
|
316
|
+
}
|
|
317
|
+
const gatewayPort = typeof runtime.gatewayActualPort === 'number' ? runtime.gatewayActualPort : values.port;
|
|
318
|
+
const result = await caddy.start({ domain, httpsPort: rawPort, gatewayPort });
|
|
319
|
+
// The start result may carry a failure detail — never the token.
|
|
320
|
+
responseJson(res, 200, { ok: result.ok, ...(result.ok ? { caddyfile: result.caddyfile } : { error: result.error, detail: result.detail, installUrl: result.installUrl }) });
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
responseJson(res, 404, { ok: false, error: { code: 'not-found', message: 'Unknown wizard route' } });
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (route.startsWith(`${WIZARD_ROUTE_PREFIX}/tunnel/`)) {
|
|
327
|
+
// Same discipline as the caddy block: walls, then method shape,
|
|
328
|
+
// then the wiring check.
|
|
329
|
+
if (method === 'POST' && !sameOriginPost(req)) {
|
|
330
|
+
responseJson(res, 403, {
|
|
331
|
+
ok: false,
|
|
332
|
+
error: { code: 'origin-rejected', message: 'The request must originate from this DSH Web application' },
|
|
333
|
+
});
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
if (method !== 'GET' && method !== 'POST') {
|
|
337
|
+
res.setHeader('Allow', 'GET, POST');
|
|
338
|
+
responseJson(res, 405, { ok: false, error: { code: 'method-not-allowed', message: 'Use GET or POST' } });
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const tunnel = options.tunnel;
|
|
342
|
+
if (tunnel === undefined) {
|
|
343
|
+
responseJson(res, 501, { ok: false, error: { code: 'not-implemented', message: 'This wizard step is not wired up yet' } });
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (route === WIZARD_TUNNEL_STATUS_ROUTE && method === 'GET') {
|
|
347
|
+
responseJson(res, 200, { ok: true, state: tunnel.status() });
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (method !== 'POST') {
|
|
351
|
+
res.setHeader('Allow', 'GET, POST');
|
|
352
|
+
responseJson(res, 405, { ok: false, error: { code: 'method-not-allowed', message: 'Use GET or POST' } });
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const body = await readJsonObject(req);
|
|
356
|
+
const domain = typeof body?.domain === 'string' ? body.domain : '';
|
|
357
|
+
if (route === WIZARD_TUNNEL_CHECK_ROUTE) {
|
|
358
|
+
const result = await tunnel.checkDomain(domain);
|
|
359
|
+
responseJson(res, 200, { ok: result.ok, ...(result.ok ? { onCloudflare: result.onCloudflare, zoneId: result.zoneId } : { reason: result.reason, detail: result.detail }) });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (route === WIZARD_TUNNEL_STOP_ROUTE && method !== 'POST') {
|
|
363
|
+
res.setHeader('Allow', 'GET, POST');
|
|
364
|
+
responseJson(res, 405, { ok: false, error: { code: 'method-not-allowed', message: 'Use POST to stop' } });
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (route === WIZARD_TUNNEL_STOP_ROUTE) {
|
|
368
|
+
tunnel.stop();
|
|
369
|
+
responseJson(res, 200, { ok: true, state: tunnel.status() });
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (route === WIZARD_TUNNEL_SETUP_ROUTE) {
|
|
373
|
+
const tunnelId = typeof body?.tunnelId === 'string' && body.tunnelId !== '' ? body.tunnelId : undefined;
|
|
374
|
+
const result = await tunnel.setup({ domain, tunnelId });
|
|
375
|
+
// The setup answer never carries the connector token — only its
|
|
376
|
+
// file path (a server-local fact) and the conflict list.
|
|
377
|
+
if (result.ok) {
|
|
378
|
+
responseJson(res, 200, { ok: true, tunnelId: result.tunnelId, publicUrl: result.publicUrl });
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
responseJson(res, 200, {
|
|
382
|
+
ok: false,
|
|
383
|
+
reason: result.reason,
|
|
384
|
+
...(result.detail !== undefined ? { detail: result.detail } : {}),
|
|
385
|
+
...(result.existing !== undefined ? { existing: result.existing } : {}),
|
|
386
|
+
...(result.installUrl !== undefined ? { installUrl: result.installUrl } : {}),
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
responseJson(res, 404, { ok: false, error: { code: 'not-found', message: 'Unknown wizard route' } });
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (route === WIZARD_VERIFY_ROUTE && (method === 'POST' || method === 'GET')) {
|
|
395
|
+
// Wall 3 applies to the POST; the GET is read-only polling. Both
|
|
396
|
+
// answers are 200 with the orchestration's own verdict in the
|
|
397
|
+
// body — the UI branches on `ok` / `result`, not on status codes.
|
|
398
|
+
if (method === 'POST' && !sameOriginPost(req)) {
|
|
399
|
+
responseJson(res, 403, {
|
|
400
|
+
ok: false,
|
|
401
|
+
error: { code: 'origin-rejected', message: 'The request must originate from this DSH Web application' },
|
|
402
|
+
});
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const verify = options.verify;
|
|
406
|
+
if (verify === undefined) {
|
|
407
|
+
responseJson(res, 501, { ok: false, error: { code: 'not-implemented', message: 'This wizard step is not wired up yet' } });
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (method === 'GET') {
|
|
411
|
+
responseJson(res, 200, await verify.check());
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const body = await readJsonObject(req);
|
|
415
|
+
if (body === null) {
|
|
416
|
+
responseJson(res, 400, { ok: false, error: { code: 'bad-request', message: 'request body must be a JSON object' } });
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
responseJson(res, 200, await verify.start({
|
|
420
|
+
publicUrl: body.publicUrl,
|
|
421
|
+
...(body.probeOnly === true ? { probeOnly: true } : {}),
|
|
422
|
+
}));
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (route.startsWith(`${WIZARD_ROUTE_PREFIX}/`)) {
|
|
426
|
+
if (method === 'POST') {
|
|
427
|
+
if (!sameOriginPost(req)) {
|
|
428
|
+
responseJson(res, 403, {
|
|
429
|
+
ok: false,
|
|
430
|
+
error: { code: 'origin-rejected', message: 'The request must originate from this DSH Web application' },
|
|
431
|
+
});
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
// Skeleton placeholder: later tasks mount the real verbs here.
|
|
435
|
+
responseJson(res, 501, {
|
|
436
|
+
ok: false,
|
|
437
|
+
error: { code: 'not-implemented', message: 'This wizard step is not wired up yet' },
|
|
438
|
+
});
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (method === 'GET') {
|
|
442
|
+
// Unknown GET under the wizard prefix: 404, after the walls.
|
|
443
|
+
responseJson(res, 404, { ok: false, error: { code: 'not-found', message: 'Unknown wizard route' } });
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
res.setHeader('Allow', 'GET, POST');
|
|
447
|
+
responseJson(res, 405, { ok: false, error: { code: 'method-not-allowed', message: 'Use GET or POST' } });
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
responseJson(res, 404, { ok: false, error: { code: 'not-found', message: 'Unknown wizard route' } });
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
if (!res.headersSent) {
|
|
454
|
+
responseJson(res, 500, { ok: false, error: { code: 'internal', message: 'The wizard route failed' } });
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
res.destroy();
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/wizard/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reverse-proxy source detection (GW-5), pure logic — the browser-facing
|
|
3
|
+
* half of the 「检测到反代从 {ip} 连过来,自动信任?」flow.
|
|
4
|
+
*
|
|
5
|
+
* The gateway (lib/lan-gate-server.cjs) records the last request that
|
|
6
|
+
* carried proxy headers from a socket it does NOT trust — almost always
|
|
7
|
+
* the user's own reverse proxy whose IP never made it into
|
|
8
|
+
* `trustedProxies` — as `lastForwardedSource: { ip, at }` in its
|
|
9
|
+
* `/lan-gate/status` answer. {@link detectForwardedSource} lifts that
|
|
10
|
+
* field out (defensively: the gateway may be old and send nothing), and
|
|
11
|
+
* {@link mergeProxyList} / {@link normalizeTrustedIp} build the merged
|
|
12
|
+
* trust list the trust-proxy route persists.
|
|
13
|
+
*
|
|
14
|
+
* Pure: no imports, no I/O — the check scripts can drive it directly.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Extract the untrusted-proxy detection from one `/lan-gate/status` body.
|
|
18
|
+
* Returns null when the gateway predates the field or the field is
|
|
19
|
+
* malformed — a missing detection is the common case, never an error.
|
|
20
|
+
*/
|
|
21
|
+
export function detectForwardedSource(gatewayStatus) {
|
|
22
|
+
if (gatewayStatus === null || typeof gatewayStatus !== 'object')
|
|
23
|
+
return null;
|
|
24
|
+
const source = gatewayStatus.lastForwardedSource;
|
|
25
|
+
if (source === null || typeof source !== 'object')
|
|
26
|
+
return null;
|
|
27
|
+
const { ip, at } = source;
|
|
28
|
+
if (typeof ip !== 'string' || ip === '')
|
|
29
|
+
return null;
|
|
30
|
+
if (typeof at !== 'number' || !Number.isFinite(at))
|
|
31
|
+
return null;
|
|
32
|
+
return { ip, at };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* One trust-list entry: an IP literal, normalized the way the gateway
|
|
36
|
+
* normalizes socket peers (`::ffff:` IPv4-in-IPv6 unwrapped). Refuses
|
|
37
|
+
* anything that is not a plausible IPv4/IPv6 literal — the value goes
|
|
38
|
+
* into persisted config and later into an exact string comparison
|
|
39
|
+
* against socket peers, where a hostname or a CIDR would silently never
|
|
40
|
+
* match.
|
|
41
|
+
*/
|
|
42
|
+
export function normalizeTrustedIp(raw) {
|
|
43
|
+
if (typeof raw !== 'string')
|
|
44
|
+
return undefined;
|
|
45
|
+
const value = raw.trim();
|
|
46
|
+
if (value === '')
|
|
47
|
+
return undefined;
|
|
48
|
+
// IPv4-in-IPv6 form the gateway itself strips from socket peers.
|
|
49
|
+
const unwrapped = value.toLowerCase().startsWith('::ffff:') ? value.slice(7) : value;
|
|
50
|
+
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(unwrapped)) {
|
|
51
|
+
return unwrapped.split('.').every((part) => Number(part) <= 255) ? unwrapped : undefined;
|
|
52
|
+
}
|
|
53
|
+
// IPv6: hex groups and colons only, at least one colon, no zone index.
|
|
54
|
+
if (/^[0-9a-f:]+$/i.test(unwrapped) && unwrapped.includes(':') && !unwrapped.includes('%')) {
|
|
55
|
+
return unwrapped.toLowerCase();
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
/** Split the comma-separated trustedProxies value into entries. */
|
|
60
|
+
export function parseProxyList(value) {
|
|
61
|
+
if (typeof value !== 'string')
|
|
62
|
+
return [];
|
|
63
|
+
return value.split(',').map((entry) => entry.trim()).filter((entry) => entry !== '');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Append one IP to the comma-separated list, preserving order, skipping
|
|
67
|
+
* duplicates (the trust-proxy route answers "already trusted" instead of
|
|
68
|
+
* rewriting). Normalization is the caller's job — the route normalizes
|
|
69
|
+
* before merging.
|
|
70
|
+
*/
|
|
71
|
+
export function mergeProxyList(current, ip) {
|
|
72
|
+
const entries = parseProxyList(current);
|
|
73
|
+
if (entries.includes(ip))
|
|
74
|
+
return entries.join(',');
|
|
75
|
+
return [...entries, ip].join(',');
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=source-detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-detect.js","sourceRoot":"","sources":["../../src/wizard/source-detect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiBH;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,aAAsB;IAC1D,IAAI,aAAa,KAAK,IAAI,IAAI,OAAO,aAAa,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC5E,MAAM,MAAM,GAAI,aAAmD,CAAC,mBAAmB,CAAA;IACvF,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC9D,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,MAA8B,CAAA;IACjD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IACpD,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/D,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,CAAA;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAC7C,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IACxB,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,SAAS,CAAA;IAClC,iEAAiE;IACjE,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IACpF,IAAI,2BAA2B,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1F,CAAC;IACD,uEAAuE;IACvE,IAAI,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3F,OAAO,SAAS,CAAC,WAAW,EAAE,CAAA;IAChC,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAA;IACxC,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC,CAAA;AACtF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB,EAAE,EAAU;IACzD,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;IACvC,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAClD,OAAO,CAAC,GAAG,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACnC,CAAC"}
|