unplugin-https-reverse-proxy 4.0.3 → 4.1.0-beta.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.
Files changed (53) hide show
  1. package/README.md +30 -6
  2. package/dist/{chunk-MD5CVZP7.js → chunk-7CA3EIIX.js} +682 -224
  3. package/dist/{chunk-RSBXFJ3Z.js → chunk-C5A7XLKL.js} +9 -2
  4. package/dist/{chunk-2P7SUCQ3.js → chunk-EY3HXBJ4.js} +0 -26
  5. package/dist/chunk-RKHJN6KY.js +53 -0
  6. package/dist/esbuild.cjs +790 -303
  7. package/dist/esbuild.js +4 -3
  8. package/dist/hmr-sse-bridge.cjs +12 -4
  9. package/dist/hmr-sse-bridge.d.cts +4 -3
  10. package/dist/hmr-sse-bridge.d.ts +4 -3
  11. package/dist/hmr-sse-bridge.js +5 -3
  12. package/dist/index.cjs +790 -303
  13. package/dist/index.js +4 -3
  14. package/dist/nuxt.cjs +787 -292
  15. package/dist/nuxt.js +5 -9
  16. package/dist/privileged-helper.cjs +668 -0
  17. package/dist/privileged-helper.d.cts +2 -0
  18. package/dist/privileged-helper.d.ts +2 -0
  19. package/dist/privileged-helper.js +641 -0
  20. package/dist/rollup.cjs +790 -303
  21. package/dist/rollup.js +4 -3
  22. package/dist/rspack.cjs +790 -303
  23. package/dist/rspack.js +4 -3
  24. package/dist/utils.cjs +0 -28
  25. package/dist/utils.d.cts +1 -3
  26. package/dist/utils.d.ts +1 -3
  27. package/dist/utils.js +1 -5
  28. package/dist/vite.cjs +790 -303
  29. package/dist/vite.js +4 -3
  30. package/dist/webpack.cjs +790 -303
  31. package/dist/webpack.js +4 -3
  32. package/package.json +1 -4
  33. package/src/caddy/caddyfile.ts +1 -0
  34. package/src/caddy/index.ts +183 -110
  35. package/src/caddy/privileged.ts +118 -0
  36. package/src/caddy/utils.ts +0 -43
  37. package/src/hmr-sse-bridge.ts +14 -3
  38. package/src/index.ts +4 -18
  39. package/src/nuxt.ts +1 -6
  40. package/src/privileged/bootstrap.ts +174 -0
  41. package/src/privileged/client.ts +287 -0
  42. package/src/privileged/entry.ts +45 -0
  43. package/src/privileged/hosts-file.ts +142 -0
  44. package/src/privileged/launch.ts +193 -0
  45. package/src/privileged/ports.ts +45 -0
  46. package/src/privileged/protocol.ts +111 -0
  47. package/src/privileged/server.ts +465 -0
  48. package/src/privileged/session.ts +114 -0
  49. package/src/privileged/validation.ts +38 -0
  50. package/src/privileged/windows-elevation.ts +80 -0
  51. package/src/privileged-helper.ts +109 -0
  52. package/src/utils.ts +0 -28
  53. package/src/host/index.ts +0 -52
package/dist/nuxt.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  vitePrintUrls
3
- } from "./chunk-MD5CVZP7.js";
4
- import "./chunk-RSBXFJ3Z.js";
3
+ } from "./chunk-7CA3EIIX.js";
5
4
  import {
6
- consola,
7
- isAdmin
8
- } from "./chunk-2P7SUCQ3.js";
5
+ consola
6
+ } from "./chunk-EY3HXBJ4.js";
7
+ import "./chunk-C5A7XLKL.js";
8
+ import "./chunk-RKHJN6KY.js";
9
9
 
10
10
  // src/nuxt.ts
11
11
  import process from "node:process";
@@ -25,10 +25,6 @@ var nuxt_default = defineNuxtModule({
25
25
  } = options;
26
26
  if (!enable)
27
27
  return;
28
- if (!isAdmin()) {
29
- consola.warn("please run as administrator");
30
- return;
31
- }
32
28
  if (!target) {
33
29
  consola.fail("please provide target");
34
30
  return;
@@ -0,0 +1,668 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/privileged-helper.ts
26
+ var import_node_process3 = __toESM(require("process"), 1);
27
+
28
+ // src/privileged/bootstrap.ts
29
+ var import_node_buffer = require("buffer");
30
+ var import_node_fs2 = require("fs");
31
+ var import_node_net3 = require("net");
32
+ var import_node_process2 = __toESM(require("process"), 1);
33
+
34
+ // src/privileged/server.ts
35
+ var import_node_child_process = require("child_process");
36
+ var import_node_fs = require("fs");
37
+ var import_node_os = require("os");
38
+ var import_node_path = require("path");
39
+ var import_node_process = __toESM(require("process"), 1);
40
+ var import_node_util = require("util");
41
+
42
+ // src/privileged/hosts-file.ts
43
+ var import_node_net = require("net");
44
+ var HOSTS_MARKER = "# uhrp";
45
+ var HOSTS_MARKER_PATTERN = /#\s*uhrp\b/;
46
+ function detectEol(content) {
47
+ return content.includes("\r\n") ? "\r\n" : "\n";
48
+ }
49
+ function toBodyLines(content) {
50
+ const lines = content.split(/\r?\n/);
51
+ if (lines.length > 0 && lines[lines.length - 1] === "")
52
+ lines.pop();
53
+ return lines;
54
+ }
55
+ function parseLine(line) {
56
+ const withoutComment = line.replace(/#.*/, "").trim();
57
+ if (!withoutComment)
58
+ return void 0;
59
+ const [ip, ...hosts] = withoutComment.split(/\s+/);
60
+ if (!ip || hosts.length === 0)
61
+ return void 0;
62
+ return { ip, hosts };
63
+ }
64
+ function isOwned(line) {
65
+ return HOSTS_MARKER_PATTERN.test(line);
66
+ }
67
+ function mentionsHost(entry, host) {
68
+ return entry.hosts.some((candidate) => candidate.toLowerCase() === host.toLowerCase());
69
+ }
70
+ function sameIpFamily(left, right) {
71
+ const leftVersion = (0, import_node_net.isIP)(left);
72
+ return leftVersion !== 0 && leftVersion === (0, import_node_net.isIP)(right);
73
+ }
74
+ function rebuild(lines, original, eol) {
75
+ const content = lines.length > 0 ? lines.join(eol) + eol : "";
76
+ return { content, changed: content !== original };
77
+ }
78
+ function addHost(content, ip, host) {
79
+ var _a;
80
+ const eol = detectEol(content);
81
+ const lines = [];
82
+ for (const line of toBodyLines(content)) {
83
+ const entry = parseLine(line);
84
+ if (!entry || !mentionsHost(entry, host) || !sameIpFamily(entry.ip, ip)) {
85
+ lines.push(line);
86
+ continue;
87
+ }
88
+ const remaining = entry.hosts.filter((candidate) => candidate.toLowerCase() !== host.toLowerCase());
89
+ if (remaining.length === 0)
90
+ continue;
91
+ const comment = ((_a = line.match(/#.*/)) == null ? void 0 : _a[0]) ?? "";
92
+ const rebuilt = `${entry.ip} ${remaining.join(" ")}${comment ? ` ${comment}` : ""}`;
93
+ lines.push(rebuilt);
94
+ }
95
+ lines.push(`${ip} ${host} ${HOSTS_MARKER}`);
96
+ return rebuild(lines, content, eol);
97
+ }
98
+ function removeHost(content, ip, host) {
99
+ var _a;
100
+ const eol = detectEol(content);
101
+ const lines = [];
102
+ for (const line of toBodyLines(content)) {
103
+ const entry = parseLine(line);
104
+ if (!entry || entry.ip !== ip || !mentionsHost(entry, host)) {
105
+ lines.push(line);
106
+ continue;
107
+ }
108
+ const remaining = entry.hosts.filter((candidate) => candidate.toLowerCase() !== host.toLowerCase());
109
+ if (remaining.length === 0)
110
+ continue;
111
+ const comment = ((_a = line.match(/#.*/)) == null ? void 0 : _a[0]) ?? "";
112
+ lines.push(`${entry.ip} ${remaining.join(" ")}${comment ? ` ${comment}` : ""}`);
113
+ }
114
+ return rebuild(lines, content, eol);
115
+ }
116
+ function removeMarkedHosts(content) {
117
+ const eol = detectEol(content);
118
+ const lines = toBodyLines(content).filter((line) => !isOwned(line));
119
+ return rebuild(lines, content, eol);
120
+ }
121
+
122
+ // src/privileged/ports.ts
123
+ function parseListeningPids(output) {
124
+ const pids = /* @__PURE__ */ new Set();
125
+ for (const line of output.split(/\r?\n/)) {
126
+ const trimmed = line.trim();
127
+ if (!trimmed)
128
+ continue;
129
+ if (!/^\d+$/.test(trimmed))
130
+ continue;
131
+ const pid = Number(trimmed);
132
+ if (Number.isSafeInteger(pid) && pid > 0)
133
+ pids.add(pid);
134
+ }
135
+ return [...pids];
136
+ }
137
+ function parseWindowsListeningPids(output, port) {
138
+ const pids = /* @__PURE__ */ new Set();
139
+ const suffix = `:${port}`;
140
+ for (const line of output.split(/\r?\n/)) {
141
+ const columns = line.trim().split(/\s+/);
142
+ if (columns.length < 5)
143
+ continue;
144
+ const protocol = columns[0].toUpperCase();
145
+ if (protocol !== "TCP")
146
+ continue;
147
+ if (!columns[1].endsWith(suffix))
148
+ continue;
149
+ if (columns[3].toUpperCase() !== "LISTENING")
150
+ continue;
151
+ const pid = Number(columns[4]);
152
+ if (Number.isSafeInteger(pid) && pid > 0)
153
+ pids.add(pid);
154
+ }
155
+ return [...pids];
156
+ }
157
+
158
+ // src/privileged/protocol.ts
159
+ function serializeMessage(message) {
160
+ return `${JSON.stringify(message)}
161
+ `;
162
+ }
163
+
164
+ // src/privileged/validation.ts
165
+ var import_node_net2 = require("net");
166
+ var ALLOWED_FREE_PORTS = /* @__PURE__ */ new Set([80, 443]);
167
+ var HOSTNAME_PATTERN = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
168
+ var IPV6_IN_HOSTS = /^[0-9a-f:.]+$/i;
169
+ function isSafeHost(host) {
170
+ if (typeof host !== "string" || host.length === 0 || host.length > 253)
171
+ return false;
172
+ if (/[\s#\0]/.test(host))
173
+ return false;
174
+ return HOSTNAME_PATTERN.test(host);
175
+ }
176
+ function isSafeIp(ip) {
177
+ if (typeof ip !== "string")
178
+ return false;
179
+ if ((0, import_node_net2.isIP)(ip) !== 0)
180
+ return true;
181
+ return IPV6_IN_HOSTS.test(ip);
182
+ }
183
+ function isAllowedFreePort(port) {
184
+ return typeof port === "number" && Number.isInteger(port) && ALLOWED_FREE_PORTS.has(port);
185
+ }
186
+
187
+ // src/privileged/server.ts
188
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
189
+ function execFileQuiet(file, args) {
190
+ return new Promise((resolve) => {
191
+ (0, import_node_child_process.execFile)(file, args, (error, stdout) => resolve({ stdout: error && !stdout ? "" : stdout }));
192
+ });
193
+ }
194
+ var HOSTS_PATH = (0, import_node_os.platform)() === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts";
195
+ var FLUSH_COMMANDS = {
196
+ win32: [{ file: "ipconfig", args: ["/flushdns"] }],
197
+ darwin: [{ file: "killall", args: ["-HUP", "mDNSResponder"] }],
198
+ // systemd-resolved is the common case; the legacy name covers older distros
199
+ // and hosts without resolved simply have no cache to drop.
200
+ linux: [
201
+ { file: "resolvectl", args: ["flush-caches"] },
202
+ { file: "systemd-resolve", args: ["--flush-caches"] }
203
+ ]
204
+ };
205
+ var FLUSH_FALLBACK = [{ file: "killall", args: ["-HUP", "mDNSResponder"] }];
206
+ async function flushDnsBestEffort(deps) {
207
+ try {
208
+ await deps.flushDns();
209
+ } catch (error) {
210
+ deps.log(`dns flush failed: ${error.message}`);
211
+ }
212
+ }
213
+ function createHelperRuntime(deps) {
214
+ const clients = /* @__PURE__ */ new Set();
215
+ let teardown;
216
+ let generation = 0;
217
+ let caddy;
218
+ const broadcast = (event) => {
219
+ const line = serializeMessage(event);
220
+ for (const client of clients) {
221
+ const socket = client;
222
+ if (typeof socket.write === "function" && !socket.destroyed)
223
+ socket.write(line);
224
+ }
225
+ };
226
+ async function stopCaddy() {
227
+ const current = caddy;
228
+ caddy = void 0;
229
+ if (current) {
230
+ try {
231
+ current.kill("SIGTERM");
232
+ } catch {
233
+ }
234
+ }
235
+ await deps.stopCaddy().catch(() => {
236
+ });
237
+ if (deps.caddyStoragePath && deps.storageOwner)
238
+ await reclaimTree(deps.caddyStoragePath, deps.storageOwner.uid, deps.storageOwner.gid).catch(() => {
239
+ });
240
+ }
241
+ function dispose() {
242
+ if (!teardown) {
243
+ const startedAt = generation;
244
+ teardown = (async () => {
245
+ try {
246
+ if (generation === startedAt) {
247
+ const edited = removeMarkedHosts(deps.readHosts());
248
+ if (edited.changed) {
249
+ deps.writeHosts(edited.content);
250
+ await deps.flushDns().catch(() => {
251
+ });
252
+ }
253
+ }
254
+ } catch (error) {
255
+ deps.log(`hosts cleanup failed: ${error.message}`);
256
+ }
257
+ if (generation === startedAt)
258
+ await stopCaddy();
259
+ })();
260
+ }
261
+ return teardown;
262
+ }
263
+ async function sweepStaleEntries() {
264
+ try {
265
+ const edited = removeMarkedHosts(deps.readHosts());
266
+ if (!edited.changed)
267
+ return false;
268
+ deps.writeHosts(edited.content);
269
+ await flushDnsBestEffort(deps);
270
+ return true;
271
+ } catch (error) {
272
+ deps.log(`stale hosts cleanup failed: ${error.message}`);
273
+ return false;
274
+ }
275
+ }
276
+ async function handle(request) {
277
+ try {
278
+ switch (request.method) {
279
+ case "ping":
280
+ return { id: request.id, ok: true, result: { pid: import_node_process.default.pid } };
281
+ case "hosts.add": {
282
+ const { ip, host } = request.params;
283
+ if (!isSafeIp(ip) || !isSafeHost(host))
284
+ return { id: request.id, ok: false, error: { message: "invalid hosts entry" } };
285
+ const edited = addHost(deps.readHosts(), ip, host);
286
+ if (edited.changed)
287
+ deps.writeHosts(edited.content);
288
+ await flushDnsBestEffort(deps);
289
+ return { id: request.id, ok: true };
290
+ }
291
+ case "hosts.remove": {
292
+ const { ip, host } = request.params;
293
+ if (!isSafeIp(ip) || !isSafeHost(host))
294
+ return { id: request.id, ok: false, error: { message: "invalid hosts entry" } };
295
+ const edited = removeHost(deps.readHosts(), ip, host);
296
+ if (edited.changed)
297
+ deps.writeHosts(edited.content);
298
+ await flushDnsBestEffort(deps);
299
+ return { id: request.id, ok: true };
300
+ }
301
+ case "port.free": {
302
+ const { port } = request.params;
303
+ if (!isAllowedFreePort(port))
304
+ return { id: request.id, ok: false, error: { message: `port ${port} is not releasable` } };
305
+ const killed = await deps.freePort(port);
306
+ return { id: request.id, ok: true, result: { killed } };
307
+ }
308
+ case "caddy.start": {
309
+ if (caddy)
310
+ await stopCaddy();
311
+ caddy = deps.startCaddy(broadcast);
312
+ return { id: request.id, ok: true, result: { pid: caddy.pid } };
313
+ }
314
+ case "caddy.stop": {
315
+ await stopCaddy();
316
+ return { id: request.id, ok: true };
317
+ }
318
+ case "ca.trust": {
319
+ const result = await deps.trustLocalCa();
320
+ return { id: request.id, ok: true, result };
321
+ }
322
+ default:
323
+ return { id: request.id, ok: false, error: { message: "unknown method" } };
324
+ }
325
+ } catch (error) {
326
+ return { id: request.id, ok: false, error: { message: error.message } };
327
+ }
328
+ }
329
+ return {
330
+ handle,
331
+ attach(client) {
332
+ clients.add(client);
333
+ generation++;
334
+ teardown = void 0;
335
+ },
336
+ async detach(client) {
337
+ clients.delete(client);
338
+ if (clients.size === 0)
339
+ await dispose();
340
+ },
341
+ dispose,
342
+ sweepStaleEntries
343
+ };
344
+ }
345
+ async function reclaimTree(target, uid, gid) {
346
+ const reclaimed = [];
347
+ const walk = (path) => {
348
+ let stats;
349
+ try {
350
+ stats = (0, import_node_fs.lstatSync)(path);
351
+ } catch {
352
+ return;
353
+ }
354
+ try {
355
+ (0, import_node_fs.lchownSync)(path, uid, gid);
356
+ reclaimed.push(path);
357
+ } catch {
358
+ }
359
+ if (!stats.isDirectory())
360
+ return;
361
+ let entries;
362
+ try {
363
+ entries = (0, import_node_fs.readdirSync)(path);
364
+ } catch {
365
+ return;
366
+ }
367
+ for (const entry of entries)
368
+ walk((0, import_node_path.join)(path, entry));
369
+ };
370
+ walk(target);
371
+ return reclaimed;
372
+ }
373
+ async function trustLocalCaWithSystemTools(caddyBin) {
374
+ try {
375
+ const { stdout, stderr } = await execFileAsync(
376
+ caddyBin,
377
+ ["trust", "--ca", "local", "--address", "127.0.0.1:2019"],
378
+ { timeout: 12e4 }
379
+ );
380
+ return { trusted: true, output: `${stdout}${stderr}`.trim() };
381
+ } catch (error) {
382
+ const output = String(error.stdout ?? "") + String(error.stderr ?? "");
383
+ if (/already|not modified|no changes/i.test(output))
384
+ return { trusted: true, output: output.trim() };
385
+ throw new Error(output.trim() || error.message);
386
+ }
387
+ }
388
+ function createNodeHelperDeps(options) {
389
+ const hostsPath = options.hostsPath ?? HOSTS_PATH;
390
+ const log = options.log ?? ((message) => import_node_process.default.stdout.write(`${message}
391
+ `));
392
+ let caddy;
393
+ return {
394
+ readHosts: () => (0, import_node_fs.readFileSync)(hostsPath, "utf8"),
395
+ writeHosts: (content) => {
396
+ (0, import_node_fs.writeFileSync)(hostsPath, content);
397
+ },
398
+ flushDns: async () => {
399
+ const candidates = FLUSH_COMMANDS[(0, import_node_os.platform)()] ?? [...FLUSH_FALLBACK];
400
+ let lastError;
401
+ for (const candidate of candidates) {
402
+ try {
403
+ await execFileAsync(candidate.file, candidate.args);
404
+ return;
405
+ } catch (error) {
406
+ lastError = error;
407
+ }
408
+ }
409
+ throw lastError ?? new Error("no DNS cache flush command for this platform");
410
+ },
411
+ freePort: async (port) => {
412
+ const pids = (0, import_node_os.platform)() === "win32" ? parseWindowsListeningPids((await execFileAsync("netstat", ["-ano"])).stdout, port) : parseListeningPids((await execFileQuiet("lsof", [`-ti:${port}`, "-sTCP:LISTEN"])).stdout);
413
+ const killed = [];
414
+ for (const pid of pids) {
415
+ if (pid === import_node_process.default.pid)
416
+ continue;
417
+ try {
418
+ import_node_process.default.kill(pid, "SIGTERM");
419
+ killed.push(pid);
420
+ } catch {
421
+ }
422
+ }
423
+ return killed;
424
+ },
425
+ caddyBin: options.caddyBin,
426
+ caddyConfigPath: options.caddyConfigPath,
427
+ caddyCwd: options.caddyCwd ?? import_node_process.default.cwd(),
428
+ caddyEnv: options.caddyEnv ?? {},
429
+ trustLocalCa: () => trustLocalCaWithSystemTools(options.caddyBin),
430
+ caddyStoragePath: options.caddyStoragePath,
431
+ storageOwner: options.storageOwner,
432
+ startCaddy: (emit) => {
433
+ var _a, _b;
434
+ const child = (0, import_node_child_process.spawn)(options.caddyBin, ["run", "--adapter", "caddyfile", "--config", options.caddyConfigPath], {
435
+ cwd: options.caddyCwd ?? import_node_process.default.cwd(),
436
+ env: { ...import_node_process.default.env, ...options.caddyEnv },
437
+ stdio: ["ignore", "pipe", "pipe"]
438
+ });
439
+ (_a = child.stdout) == null ? void 0 : _a.on("data", (data) => emit({ type: "event", kind: "caddy.stdout", data: data.toString() }));
440
+ (_b = child.stderr) == null ? void 0 : _b.on("data", (data) => emit({ type: "event", kind: "caddy.stderr", data: data.toString() }));
441
+ child.on("exit", (code, signal) => emit({ type: "event", kind: "caddy.exit", code, signal }));
442
+ caddy = child;
443
+ return caddy;
444
+ },
445
+ stopCaddy: async () => {
446
+ const current = caddy;
447
+ caddy = void 0;
448
+ if (!current)
449
+ return;
450
+ try {
451
+ current.kill("SIGTERM");
452
+ } catch {
453
+ }
454
+ },
455
+ log
456
+ };
457
+ }
458
+ function attachSocket(socket, runtime, log) {
459
+ let buffer = "";
460
+ runtime.attach(socket);
461
+ socket.on("data", (chunk) => {
462
+ buffer += chunk.toString("utf8");
463
+ let index = buffer.indexOf("\n");
464
+ while (index >= 0) {
465
+ const line = buffer.slice(0, index);
466
+ buffer = buffer.slice(index + 1);
467
+ index = buffer.indexOf("\n");
468
+ if (!line.trim())
469
+ continue;
470
+ let request;
471
+ try {
472
+ request = JSON.parse(line);
473
+ } catch (error) {
474
+ log(`invalid request: ${error.message}`);
475
+ continue;
476
+ }
477
+ void runtime.handle(request).then((response) => {
478
+ if (!socket.destroyed)
479
+ socket.write(serializeMessage(response));
480
+ });
481
+ }
482
+ });
483
+ socket.on("error", (error) => {
484
+ log(`connection error: ${error.message}`);
485
+ });
486
+ socket.on("close", () => {
487
+ void runtime.detach(socket);
488
+ });
489
+ }
490
+
491
+ // src/privileged/bootstrap.ts
492
+ var HANDSHAKE_TIMEOUT_MS = 12e4;
493
+ async function startHelper(options) {
494
+ const { socketPath, token } = options;
495
+ const log = options.log ?? ((message) => import_node_process2.default.stdout.write(`[uhrp-helper] ${message}
496
+ `));
497
+ const deps = options.deps ?? createNodeHelperDeps({
498
+ hostsPath: options.hostsPath,
499
+ log,
500
+ caddyBin: options.caddyBin,
501
+ caddyConfigPath: options.caddyConfigPath,
502
+ caddyCwd: options.caddyCwd,
503
+ caddyEnv: options.caddyEnv,
504
+ caddyStoragePath: options.caddyStoragePath,
505
+ storageOwner: typeof options.ownerUid === "number" ? { uid: options.ownerUid, gid: options.ownerGid ?? options.ownerUid } : void 0
506
+ });
507
+ const runtime = createHelperRuntime(deps);
508
+ await runtime.sweepStaleEntries().catch(() => {
509
+ });
510
+ try {
511
+ (0, import_node_fs2.rmSync)(socketPath, { force: true });
512
+ } catch {
513
+ }
514
+ const connections = /* @__PURE__ */ new Set();
515
+ const server = (0, import_node_net3.createServer)((socket) => {
516
+ var _a;
517
+ let buffer = "";
518
+ let authenticated = false;
519
+ connections.add(socket);
520
+ socket.on("close", () => connections.delete(socket));
521
+ const timer = setTimeout(() => {
522
+ if (!authenticated)
523
+ socket.destroy();
524
+ }, HANDSHAKE_TIMEOUT_MS);
525
+ (_a = timer.unref) == null ? void 0 : _a.call(timer);
526
+ const onData = (chunk) => {
527
+ buffer += chunk.toString("utf8");
528
+ const index = buffer.indexOf("\n");
529
+ if (index < 0)
530
+ return;
531
+ const line = buffer.slice(0, index);
532
+ const rest = buffer.slice(index + 1);
533
+ let parsed;
534
+ try {
535
+ parsed = JSON.parse(line);
536
+ } catch {
537
+ socket.destroy();
538
+ return;
539
+ }
540
+ if (parsed.type !== "handshake" || typeof parsed.token !== "string") {
541
+ socket.destroy();
542
+ return;
543
+ }
544
+ clearTimeout(timer);
545
+ socket.removeListener("data", onData);
546
+ if (parsed.token !== token) {
547
+ socket.end(`${JSON.stringify({ type: "handshake", ok: false, reason: "invalid token" })}
548
+ `);
549
+ return;
550
+ }
551
+ authenticated = true;
552
+ socket.write(`${JSON.stringify({ type: "handshake", ok: true, pid: import_node_process2.default.pid })}
553
+ `);
554
+ attachSocket(socket, runtime, log);
555
+ if (rest.trim() !== "")
556
+ socket.emit("data", import_node_buffer.Buffer.from(rest));
557
+ };
558
+ socket.on("data", onData);
559
+ socket.on("error", () => {
560
+ });
561
+ });
562
+ await new Promise((resolve, reject) => {
563
+ server.once("error", reject);
564
+ server.listen(socketPath, () => resolve());
565
+ });
566
+ try {
567
+ if (typeof options.ownerUid === "number") {
568
+ (0, import_node_fs2.chownSync)(socketPath, options.ownerUid, options.ownerGid ?? options.ownerUid);
569
+ (0, import_node_fs2.chmodSync)(socketPath, 384);
570
+ } else {
571
+ (0, import_node_fs2.chmodSync)(socketPath, 384);
572
+ }
573
+ } catch (error) {
574
+ log(`could not set socket ownership: ${error.message}`);
575
+ }
576
+ return {
577
+ runtime,
578
+ async close() {
579
+ for (const socket of connections)
580
+ socket.destroy();
581
+ connections.clear();
582
+ await new Promise((resolve) => {
583
+ server.close(() => resolve());
584
+ });
585
+ }
586
+ };
587
+ }
588
+
589
+ // src/privileged-helper.ts
590
+ function parseArgs(argv) {
591
+ let socket = "";
592
+ let token = "";
593
+ let hosts;
594
+ let caddyBin = "";
595
+ let caddyConfig = "";
596
+ let ownerUid;
597
+ let ownerGid;
598
+ let caddyStorage;
599
+ for (let index = 0; index < argv.length; index++) {
600
+ const current = argv[index];
601
+ if (current === "--socket")
602
+ socket = argv[++index] ?? "";
603
+ else if (current === "--token")
604
+ token = argv[++index] ?? "";
605
+ else if (current === "--hosts")
606
+ hosts = argv[++index] ?? void 0;
607
+ else if (current === "--caddy")
608
+ caddyBin = argv[++index] ?? "";
609
+ else if (current === "--caddy-config")
610
+ caddyConfig = argv[++index] ?? "";
611
+ else if (current === "--owner-uid")
612
+ ownerUid = Number(argv[++index]);
613
+ else if (current === "--owner-gid")
614
+ ownerGid = Number(argv[++index]);
615
+ else if (current === "--caddy-storage")
616
+ caddyStorage = argv[++index] ?? void 0;
617
+ }
618
+ if (!socket || !token)
619
+ throw new Error("usage: privileged-helper --socket <path> --token <token> --caddy <bin> --caddy-config <path>");
620
+ if (!caddyBin || !caddyConfig)
621
+ throw new Error("privileged-helper requires --caddy and --caddy-config");
622
+ return {
623
+ socket,
624
+ token,
625
+ hosts,
626
+ caddyBin,
627
+ caddyConfig,
628
+ ownerUid: Number.isInteger(ownerUid) ? ownerUid : void 0,
629
+ ownerGid: Number.isInteger(ownerGid) ? ownerGid : void 0,
630
+ caddyStorage
631
+ };
632
+ }
633
+ async function main() {
634
+ const args = parseArgs(import_node_process3.default.argv.slice(2));
635
+ const helper = await startHelper({
636
+ socketPath: args.socket,
637
+ token: args.token,
638
+ hostsPath: args.hosts,
639
+ caddyBin: args.caddyBin,
640
+ caddyConfigPath: args.caddyConfig,
641
+ ownerUid: args.ownerUid,
642
+ ownerGid: args.ownerGid,
643
+ caddyStoragePath: args.caddyStorage,
644
+ caddyEnv: {
645
+ HOME: import_node_process3.default.env.HOME ?? "",
646
+ // Keep Caddy's storage next to the invoking user's home even though the
647
+ // process runs as root.
648
+ XDG_DATA_HOME: import_node_process3.default.env.XDG_DATA_HOME ?? ""
649
+ }
650
+ });
651
+ import_node_process3.default.on("SIGINT", () => {
652
+ });
653
+ const shutdown = () => {
654
+ void helper.runtime.dispose().finally(() => import_node_process3.default.exit(0));
655
+ };
656
+ import_node_process3.default.on("SIGTERM", shutdown);
657
+ import_node_process3.default.on("SIGHUP", shutdown);
658
+ import_node_process3.default.stdout.on("error", () => {
659
+ });
660
+ import_node_process3.default.stderr.on("error", () => {
661
+ });
662
+ }
663
+ main().catch((error) => {
664
+ import_node_process3.default.stderr.write(`[uhrp-helper] fatal: ${error.message}
665
+ `);
666
+ import_node_process3.default.exit(1);
667
+ });
668
+ exports.default = module.exports;
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }