unplugin-https-reverse-proxy 4.0.3 → 4.1.0-beta.10
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 +30 -6
- package/dist/{chunk-RSBXFJ3Z.js → chunk-C5A7XLKL.js} +9 -2
- package/dist/{chunk-2P7SUCQ3.js → chunk-EY3HXBJ4.js} +0 -26
- package/dist/chunk-RKHJN6KY.js +53 -0
- package/dist/{chunk-MD5CVZP7.js → chunk-U7KAIPZC.js} +982 -797
- package/dist/chunk-WOMR3ZLM.js +17 -0
- package/dist/esbuild.cjs +1086 -867
- package/dist/esbuild.js +5 -3
- package/dist/hmr-sse-bridge.cjs +12 -4
- package/dist/hmr-sse-bridge.d.cts +4 -3
- package/dist/hmr-sse-bridge.d.ts +4 -3
- package/dist/hmr-sse-bridge.js +5 -3
- package/dist/index.cjs +1086 -867
- package/dist/index.js +5 -3
- package/dist/nuxt.cjs +1057 -852
- package/dist/nuxt.js +6 -9
- package/dist/privilege-state.cjs +44 -0
- package/dist/privilege-state.d.cts +7 -0
- package/dist/privilege-state.d.ts +7 -0
- package/dist/privilege-state.js +10 -0
- package/dist/privileged-helper.cjs +824 -0
- package/dist/privileged-helper.d.cts +2 -0
- package/dist/privileged-helper.d.ts +2 -0
- package/dist/privileged-helper.js +797 -0
- package/dist/rollup.cjs +1086 -867
- package/dist/rollup.js +5 -3
- package/dist/rspack.cjs +1086 -867
- package/dist/rspack.js +5 -3
- package/dist/utils.cjs +0 -28
- package/dist/utils.d.cts +1 -3
- package/dist/utils.d.ts +1 -3
- package/dist/utils.js +1 -5
- package/dist/vite.cjs +1086 -867
- package/dist/vite.js +5 -3
- package/dist/webpack.cjs +1086 -867
- package/dist/webpack.js +5 -3
- package/package.json +1 -4
- package/src/caddy/caddyfile.ts +3 -0
- package/src/caddy/config.ts +195 -53
- package/src/caddy/constants.ts +30 -1
- package/src/caddy/index.ts +239 -206
- package/src/caddy/privileged.ts +124 -0
- package/src/caddy/utils.ts +0 -43
- package/src/hmr-sse-bridge.ts +14 -3
- package/src/index.ts +52 -25
- package/src/nuxt.ts +1 -6
- package/src/privilege-state.ts +23 -0
- package/src/privileged/bootstrap.ts +289 -0
- package/src/privileged/client.ts +288 -0
- package/src/privileged/entry.ts +73 -0
- package/src/privileged/hosts-file.ts +142 -0
- package/src/privileged/launch.ts +332 -0
- package/src/privileged/path-guard.ts +74 -0
- package/src/privileged/ports.ts +45 -0
- package/src/privileged/protocol.ts +111 -0
- package/src/privileged/server.ts +578 -0
- package/src/privileged/session.ts +169 -0
- package/src/privileged/validation.ts +38 -0
- package/src/privileged/windows-elevation.ts +80 -0
- package/src/privileged-helper.ts +123 -0
- package/src/utils.ts +0 -28
- package/src/host/index.ts +0 -52
|
@@ -0,0 +1,824 @@
|
|
|
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_fs3 = require("fs");
|
|
31
|
+
var import_node_net3 = require("net");
|
|
32
|
+
var import_node_path2 = require("path");
|
|
33
|
+
var import_node_process2 = __toESM(require("process"), 1);
|
|
34
|
+
|
|
35
|
+
// src/privileged/path-guard.ts
|
|
36
|
+
var import_node_fs = require("fs");
|
|
37
|
+
function describe(stats) {
|
|
38
|
+
return `uid=${stats.uid} mode=${(stats.mode & 511).toString(8)}`;
|
|
39
|
+
}
|
|
40
|
+
function checkPrivilegedPath(path, options) {
|
|
41
|
+
let stats;
|
|
42
|
+
try {
|
|
43
|
+
stats = (0, import_node_fs.lstatSync)(path);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return { path, reason: `${options.label} is not readable: ${error.message}` };
|
|
46
|
+
}
|
|
47
|
+
if (stats.isSymbolicLink())
|
|
48
|
+
return { path, reason: `${options.label} is a symbolic link (${describe(stats)})` };
|
|
49
|
+
if (!stats.isFile() && !stats.isDirectory())
|
|
50
|
+
return { path, reason: `${options.label} is neither a file nor a directory` };
|
|
51
|
+
const allowedOwner = stats.uid === 0 || options.ownerUid !== void 0 && stats.uid === options.ownerUid;
|
|
52
|
+
if (!allowedOwner)
|
|
53
|
+
return { path, reason: `${options.label} is owned by another user (${describe(stats)})` };
|
|
54
|
+
if (stats.mode & 18)
|
|
55
|
+
return { path, reason: `${options.label} is group- or world-writable (${describe(stats)})` };
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
function describeRejection(rejection) {
|
|
59
|
+
return `refusing to start: ${rejection.reason} (${rejection.path})`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/privileged/server.ts
|
|
63
|
+
var import_node_child_process = require("child_process");
|
|
64
|
+
var import_node_fs2 = require("fs");
|
|
65
|
+
var import_node_os = require("os");
|
|
66
|
+
var import_node_path = require("path");
|
|
67
|
+
var import_node_process = __toESM(require("process"), 1);
|
|
68
|
+
var import_node_util = require("util");
|
|
69
|
+
|
|
70
|
+
// src/privileged/hosts-file.ts
|
|
71
|
+
var import_node_net = require("net");
|
|
72
|
+
var HOSTS_MARKER = "# uhrp";
|
|
73
|
+
var HOSTS_MARKER_PATTERN = /#\s*uhrp\b/;
|
|
74
|
+
function detectEol(content) {
|
|
75
|
+
return content.includes("\r\n") ? "\r\n" : "\n";
|
|
76
|
+
}
|
|
77
|
+
function toBodyLines(content) {
|
|
78
|
+
const lines = content.split(/\r?\n/);
|
|
79
|
+
if (lines.length > 0 && lines[lines.length - 1] === "")
|
|
80
|
+
lines.pop();
|
|
81
|
+
return lines;
|
|
82
|
+
}
|
|
83
|
+
function parseLine(line) {
|
|
84
|
+
const withoutComment = line.replace(/#.*/, "").trim();
|
|
85
|
+
if (!withoutComment)
|
|
86
|
+
return void 0;
|
|
87
|
+
const [ip, ...hosts] = withoutComment.split(/\s+/);
|
|
88
|
+
if (!ip || hosts.length === 0)
|
|
89
|
+
return void 0;
|
|
90
|
+
return { ip, hosts };
|
|
91
|
+
}
|
|
92
|
+
function isOwned(line) {
|
|
93
|
+
return HOSTS_MARKER_PATTERN.test(line);
|
|
94
|
+
}
|
|
95
|
+
function mentionsHost(entry, host) {
|
|
96
|
+
return entry.hosts.some((candidate) => candidate.toLowerCase() === host.toLowerCase());
|
|
97
|
+
}
|
|
98
|
+
function sameIpFamily(left, right) {
|
|
99
|
+
const leftVersion = (0, import_node_net.isIP)(left);
|
|
100
|
+
return leftVersion !== 0 && leftVersion === (0, import_node_net.isIP)(right);
|
|
101
|
+
}
|
|
102
|
+
function rebuild(lines, original, eol) {
|
|
103
|
+
const content = lines.length > 0 ? lines.join(eol) + eol : "";
|
|
104
|
+
return { content, changed: content !== original };
|
|
105
|
+
}
|
|
106
|
+
function addHost(content, ip, host) {
|
|
107
|
+
var _a;
|
|
108
|
+
const eol = detectEol(content);
|
|
109
|
+
const lines = [];
|
|
110
|
+
for (const line of toBodyLines(content)) {
|
|
111
|
+
const entry = parseLine(line);
|
|
112
|
+
if (!entry || !mentionsHost(entry, host) || !sameIpFamily(entry.ip, ip)) {
|
|
113
|
+
lines.push(line);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const remaining = entry.hosts.filter((candidate) => candidate.toLowerCase() !== host.toLowerCase());
|
|
117
|
+
if (remaining.length === 0)
|
|
118
|
+
continue;
|
|
119
|
+
const comment = ((_a = line.match(/#.*/)) == null ? void 0 : _a[0]) ?? "";
|
|
120
|
+
const rebuilt = `${entry.ip} ${remaining.join(" ")}${comment ? ` ${comment}` : ""}`;
|
|
121
|
+
lines.push(rebuilt);
|
|
122
|
+
}
|
|
123
|
+
lines.push(`${ip} ${host} ${HOSTS_MARKER}`);
|
|
124
|
+
return rebuild(lines, content, eol);
|
|
125
|
+
}
|
|
126
|
+
function removeHost(content, ip, host) {
|
|
127
|
+
var _a;
|
|
128
|
+
const eol = detectEol(content);
|
|
129
|
+
const lines = [];
|
|
130
|
+
for (const line of toBodyLines(content)) {
|
|
131
|
+
const entry = parseLine(line);
|
|
132
|
+
if (!entry || entry.ip !== ip || !mentionsHost(entry, host)) {
|
|
133
|
+
lines.push(line);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const remaining = entry.hosts.filter((candidate) => candidate.toLowerCase() !== host.toLowerCase());
|
|
137
|
+
if (remaining.length === 0)
|
|
138
|
+
continue;
|
|
139
|
+
const comment = ((_a = line.match(/#.*/)) == null ? void 0 : _a[0]) ?? "";
|
|
140
|
+
lines.push(`${entry.ip} ${remaining.join(" ")}${comment ? ` ${comment}` : ""}`);
|
|
141
|
+
}
|
|
142
|
+
return rebuild(lines, content, eol);
|
|
143
|
+
}
|
|
144
|
+
function removeMarkedHosts(content) {
|
|
145
|
+
const eol = detectEol(content);
|
|
146
|
+
const lines = toBodyLines(content).filter((line) => !isOwned(line));
|
|
147
|
+
return rebuild(lines, content, eol);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/privileged/ports.ts
|
|
151
|
+
function parseListeningPids(output) {
|
|
152
|
+
const pids = /* @__PURE__ */ new Set();
|
|
153
|
+
for (const line of output.split(/\r?\n/)) {
|
|
154
|
+
const trimmed = line.trim();
|
|
155
|
+
if (!trimmed)
|
|
156
|
+
continue;
|
|
157
|
+
if (!/^\d+$/.test(trimmed))
|
|
158
|
+
continue;
|
|
159
|
+
const pid = Number(trimmed);
|
|
160
|
+
if (Number.isSafeInteger(pid) && pid > 0)
|
|
161
|
+
pids.add(pid);
|
|
162
|
+
}
|
|
163
|
+
return [...pids];
|
|
164
|
+
}
|
|
165
|
+
function parseWindowsListeningPids(output, port) {
|
|
166
|
+
const pids = /* @__PURE__ */ new Set();
|
|
167
|
+
const suffix = `:${port}`;
|
|
168
|
+
for (const line of output.split(/\r?\n/)) {
|
|
169
|
+
const columns = line.trim().split(/\s+/);
|
|
170
|
+
if (columns.length < 5)
|
|
171
|
+
continue;
|
|
172
|
+
const protocol = columns[0].toUpperCase();
|
|
173
|
+
if (protocol !== "TCP")
|
|
174
|
+
continue;
|
|
175
|
+
if (!columns[1].endsWith(suffix))
|
|
176
|
+
continue;
|
|
177
|
+
if (columns[3].toUpperCase() !== "LISTENING")
|
|
178
|
+
continue;
|
|
179
|
+
const pid = Number(columns[4]);
|
|
180
|
+
if (Number.isSafeInteger(pid) && pid > 0)
|
|
181
|
+
pids.add(pid);
|
|
182
|
+
}
|
|
183
|
+
return [...pids];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/privileged/protocol.ts
|
|
187
|
+
function serializeMessage(message) {
|
|
188
|
+
return `${JSON.stringify(message)}
|
|
189
|
+
`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/privileged/validation.ts
|
|
193
|
+
var import_node_net2 = require("net");
|
|
194
|
+
var ALLOWED_FREE_PORTS = /* @__PURE__ */ new Set([80, 443]);
|
|
195
|
+
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;
|
|
196
|
+
var IPV6_IN_HOSTS = /^[0-9a-f:.]+$/i;
|
|
197
|
+
function isSafeHost(host) {
|
|
198
|
+
if (typeof host !== "string" || host.length === 0 || host.length > 253)
|
|
199
|
+
return false;
|
|
200
|
+
if (/[\s#\0]/.test(host))
|
|
201
|
+
return false;
|
|
202
|
+
return HOSTNAME_PATTERN.test(host);
|
|
203
|
+
}
|
|
204
|
+
function isSafeIp(ip) {
|
|
205
|
+
if (typeof ip !== "string")
|
|
206
|
+
return false;
|
|
207
|
+
if ((0, import_node_net2.isIP)(ip) !== 0)
|
|
208
|
+
return true;
|
|
209
|
+
return IPV6_IN_HOSTS.test(ip);
|
|
210
|
+
}
|
|
211
|
+
function isAllowedFreePort(port) {
|
|
212
|
+
return typeof port === "number" && Number.isInteger(port) && ALLOWED_FREE_PORTS.has(port);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/privileged/server.ts
|
|
216
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
217
|
+
function execFileQuiet(file, args) {
|
|
218
|
+
return new Promise((resolve) => {
|
|
219
|
+
(0, import_node_child_process.execFile)(file, args, (error, stdout) => resolve({ stdout: error && !stdout ? "" : stdout }));
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
var HOSTS_PATH = (0, import_node_os.platform)() === "win32" ? "C:\\Windows\\System32\\drivers\\etc\\hosts" : "/etc/hosts";
|
|
223
|
+
var FLUSH_COMMANDS = {
|
|
224
|
+
win32: [{ file: "ipconfig", args: ["/flushdns"] }],
|
|
225
|
+
darwin: [{ file: "killall", args: ["-HUP", "mDNSResponder"] }],
|
|
226
|
+
// systemd-resolved is the common case; the legacy name covers older distros
|
|
227
|
+
// and hosts without resolved simply have no cache to drop.
|
|
228
|
+
linux: [
|
|
229
|
+
{ file: "resolvectl", args: ["flush-caches"] },
|
|
230
|
+
{ file: "systemd-resolve", args: ["--flush-caches"] }
|
|
231
|
+
]
|
|
232
|
+
};
|
|
233
|
+
var FLUSH_FALLBACK = [{ file: "killall", args: ["-HUP", "mDNSResponder"] }];
|
|
234
|
+
async function flushDnsBestEffort(deps) {
|
|
235
|
+
try {
|
|
236
|
+
await deps.flushDns();
|
|
237
|
+
} catch (error) {
|
|
238
|
+
deps.log(`dns flush failed: ${error.message}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function createHelperRuntime(deps) {
|
|
242
|
+
const clients = /* @__PURE__ */ new Set();
|
|
243
|
+
let teardown;
|
|
244
|
+
let generation = 0;
|
|
245
|
+
let caddy;
|
|
246
|
+
const hostOwners = /* @__PURE__ */ new Map();
|
|
247
|
+
const hostId = (ip, host) => `${ip} ${host.toLowerCase()}`;
|
|
248
|
+
function addHostOwner(client, ip, host) {
|
|
249
|
+
const id = hostId(ip, host);
|
|
250
|
+
let owners = hostOwners.get(id);
|
|
251
|
+
if (!owners) {
|
|
252
|
+
owners = /* @__PURE__ */ new Set();
|
|
253
|
+
hostOwners.set(id, owners);
|
|
254
|
+
}
|
|
255
|
+
owners.add(client ?? deps);
|
|
256
|
+
}
|
|
257
|
+
function releaseHostOwner(client, ip, host) {
|
|
258
|
+
const id = hostId(ip, host);
|
|
259
|
+
const owners = hostOwners.get(id);
|
|
260
|
+
if (!owners)
|
|
261
|
+
return true;
|
|
262
|
+
owners.delete(client ?? deps);
|
|
263
|
+
if (owners.size > 0)
|
|
264
|
+
return false;
|
|
265
|
+
hostOwners.delete(id);
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
const broadcast = (event) => {
|
|
269
|
+
const line = serializeMessage(event);
|
|
270
|
+
for (const client of clients) {
|
|
271
|
+
const socket = client;
|
|
272
|
+
if (typeof socket.write === "function" && !socket.destroyed)
|
|
273
|
+
socket.write(line);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
async function stopCaddy() {
|
|
277
|
+
const current = caddy;
|
|
278
|
+
caddy = void 0;
|
|
279
|
+
if (current) {
|
|
280
|
+
try {
|
|
281
|
+
current.kill("SIGTERM");
|
|
282
|
+
} catch {
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
await deps.stopCaddy().catch(() => {
|
|
286
|
+
});
|
|
287
|
+
if (deps.caddyStoragePath && deps.storageOwner)
|
|
288
|
+
await reclaimTree(deps.caddyStoragePath, deps.storageOwner.uid, deps.storageOwner.gid).catch(() => {
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
function dispose() {
|
|
292
|
+
if (!teardown) {
|
|
293
|
+
const startedAt = generation;
|
|
294
|
+
teardown = (async () => {
|
|
295
|
+
try {
|
|
296
|
+
if (generation === startedAt) {
|
|
297
|
+
const edited = removeMarkedHosts(deps.readHosts());
|
|
298
|
+
if (edited.changed) {
|
|
299
|
+
deps.writeHosts(edited.content);
|
|
300
|
+
await deps.flushDns().catch(() => {
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
deps.log(`hosts cleanup failed: ${error.message}`);
|
|
306
|
+
}
|
|
307
|
+
if (generation === startedAt)
|
|
308
|
+
await stopCaddy();
|
|
309
|
+
})();
|
|
310
|
+
}
|
|
311
|
+
return teardown;
|
|
312
|
+
}
|
|
313
|
+
async function sweepStaleEntries() {
|
|
314
|
+
try {
|
|
315
|
+
const edited = removeMarkedHosts(deps.readHosts());
|
|
316
|
+
if (!edited.changed)
|
|
317
|
+
return false;
|
|
318
|
+
deps.writeHosts(edited.content);
|
|
319
|
+
await flushDnsBestEffort(deps);
|
|
320
|
+
return true;
|
|
321
|
+
} catch (error) {
|
|
322
|
+
deps.log(`stale hosts cleanup failed: ${error.message}`);
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async function handle(request, client) {
|
|
327
|
+
try {
|
|
328
|
+
switch (request.method) {
|
|
329
|
+
case "ping":
|
|
330
|
+
return { id: request.id, ok: true, result: { pid: import_node_process.default.pid } };
|
|
331
|
+
case "hosts.add": {
|
|
332
|
+
const { ip, host } = request.params;
|
|
333
|
+
if (!isSafeIp(ip) || !isSafeHost(host))
|
|
334
|
+
return { id: request.id, ok: false, error: { message: "invalid hosts entry" } };
|
|
335
|
+
const edited = addHost(deps.readHosts(), ip, host);
|
|
336
|
+
if (edited.changed)
|
|
337
|
+
deps.writeHosts(edited.content);
|
|
338
|
+
addHostOwner(client, ip, host);
|
|
339
|
+
await flushDnsBestEffort(deps);
|
|
340
|
+
return { id: request.id, ok: true };
|
|
341
|
+
}
|
|
342
|
+
case "hosts.remove": {
|
|
343
|
+
const { ip, host } = request.params;
|
|
344
|
+
if (!isSafeIp(ip) || !isSafeHost(host))
|
|
345
|
+
return { id: request.id, ok: false, error: { message: "invalid hosts entry" } };
|
|
346
|
+
const last = releaseHostOwner(client, ip, host);
|
|
347
|
+
if (!last)
|
|
348
|
+
return { id: request.id, ok: true, result: { kept: true } };
|
|
349
|
+
const edited = removeHost(deps.readHosts(), ip, host);
|
|
350
|
+
if (edited.changed)
|
|
351
|
+
deps.writeHosts(edited.content);
|
|
352
|
+
await flushDnsBestEffort(deps);
|
|
353
|
+
return { id: request.id, ok: true };
|
|
354
|
+
}
|
|
355
|
+
case "port.free": {
|
|
356
|
+
const { port } = request.params;
|
|
357
|
+
if (!isAllowedFreePort(port))
|
|
358
|
+
return { id: request.id, ok: false, error: { message: `port ${port} is not releasable` } };
|
|
359
|
+
const killed = await deps.freePort(port, caddy == null ? void 0 : caddy.pid);
|
|
360
|
+
return { id: request.id, ok: true, result: { killed } };
|
|
361
|
+
}
|
|
362
|
+
case "caddy.start": {
|
|
363
|
+
if (caddy && caddy.running !== false)
|
|
364
|
+
return { id: request.id, ok: true, result: { pid: caddy.pid, started: false } };
|
|
365
|
+
caddy = deps.startCaddy(broadcast);
|
|
366
|
+
return { id: request.id, ok: true, result: { pid: caddy.pid, started: true } };
|
|
367
|
+
}
|
|
368
|
+
case "caddy.stop": {
|
|
369
|
+
await stopCaddy();
|
|
370
|
+
return { id: request.id, ok: true };
|
|
371
|
+
}
|
|
372
|
+
case "ca.trust": {
|
|
373
|
+
const result = await deps.trustLocalCa();
|
|
374
|
+
return { id: request.id, ok: true, result };
|
|
375
|
+
}
|
|
376
|
+
default:
|
|
377
|
+
return { id: request.id, ok: false, error: { message: "unknown method" } };
|
|
378
|
+
}
|
|
379
|
+
} catch (error) {
|
|
380
|
+
return { id: request.id, ok: false, error: { message: error.message } };
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
handle,
|
|
385
|
+
attach(client) {
|
|
386
|
+
clients.add(client);
|
|
387
|
+
generation++;
|
|
388
|
+
teardown = void 0;
|
|
389
|
+
},
|
|
390
|
+
async detach(client) {
|
|
391
|
+
clients.delete(client);
|
|
392
|
+
for (const [id, owners] of [...hostOwners]) {
|
|
393
|
+
if (!owners.delete(client) || owners.size > 0)
|
|
394
|
+
continue;
|
|
395
|
+
hostOwners.delete(id);
|
|
396
|
+
const [ip, host] = id.split(" ");
|
|
397
|
+
try {
|
|
398
|
+
const edited = removeHost(deps.readHosts(), ip, host);
|
|
399
|
+
if (edited.changed) {
|
|
400
|
+
deps.writeHosts(edited.content);
|
|
401
|
+
await flushDnsBestEffort(deps);
|
|
402
|
+
}
|
|
403
|
+
} catch (error) {
|
|
404
|
+
deps.log(`hosts cleanup failed: ${error.message}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (clients.size === 0)
|
|
408
|
+
await dispose();
|
|
409
|
+
},
|
|
410
|
+
dispose,
|
|
411
|
+
sweepStaleEntries,
|
|
412
|
+
clientCount: () => clients.size
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
async function reclaimTree(target, uid, gid) {
|
|
416
|
+
const reclaimed = [];
|
|
417
|
+
const walk = (path) => {
|
|
418
|
+
let stats;
|
|
419
|
+
try {
|
|
420
|
+
stats = (0, import_node_fs2.lstatSync)(path);
|
|
421
|
+
} catch {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
(0, import_node_fs2.lchownSync)(path, uid, gid);
|
|
426
|
+
reclaimed.push(path);
|
|
427
|
+
} catch {
|
|
428
|
+
}
|
|
429
|
+
if (!stats.isDirectory())
|
|
430
|
+
return;
|
|
431
|
+
let entries;
|
|
432
|
+
try {
|
|
433
|
+
entries = (0, import_node_fs2.readdirSync)(path);
|
|
434
|
+
} catch {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
for (const entry of entries)
|
|
438
|
+
walk((0, import_node_path.join)(path, entry));
|
|
439
|
+
};
|
|
440
|
+
walk(target);
|
|
441
|
+
return reclaimed;
|
|
442
|
+
}
|
|
443
|
+
async function trustLocalCaWithSystemTools(caddyBin, adminSocket) {
|
|
444
|
+
const address = adminSocket ? `unix/${adminSocket}` : "127.0.0.1:2019";
|
|
445
|
+
try {
|
|
446
|
+
const { stdout, stderr } = await execFileAsync(
|
|
447
|
+
caddyBin,
|
|
448
|
+
["trust", "--ca", "local", "--address", address],
|
|
449
|
+
{ timeout: 12e4 }
|
|
450
|
+
);
|
|
451
|
+
return { trusted: true, output: `${stdout}${stderr}`.trim() };
|
|
452
|
+
} catch (error) {
|
|
453
|
+
const output = String(error.stdout ?? "") + String(error.stderr ?? "");
|
|
454
|
+
if (/already|not modified|no changes/i.test(output))
|
|
455
|
+
return { trusted: true, output: output.trim() };
|
|
456
|
+
throw new Error(output.trim() || error.message);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
function createNodeHelperDeps(options) {
|
|
460
|
+
const hostsPath = options.hostsPath ?? HOSTS_PATH;
|
|
461
|
+
const log = options.log ?? ((message) => import_node_process.default.stdout.write(`${message}
|
|
462
|
+
`));
|
|
463
|
+
let caddy;
|
|
464
|
+
import_node_process.default.on("exit", () => {
|
|
465
|
+
if ((caddy == null ? void 0 : caddy.pid) === void 0)
|
|
466
|
+
return;
|
|
467
|
+
try {
|
|
468
|
+
import_node_process.default.kill(caddy.pid, "SIGTERM");
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
return {
|
|
473
|
+
readHosts: () => (0, import_node_fs2.readFileSync)(hostsPath, "utf8"),
|
|
474
|
+
writeHosts: (content) => {
|
|
475
|
+
(0, import_node_fs2.writeFileSync)(hostsPath, content);
|
|
476
|
+
},
|
|
477
|
+
flushDns: async () => {
|
|
478
|
+
const candidates = FLUSH_COMMANDS[(0, import_node_os.platform)()] ?? [...FLUSH_FALLBACK];
|
|
479
|
+
let lastError;
|
|
480
|
+
for (const candidate of candidates) {
|
|
481
|
+
try {
|
|
482
|
+
await execFileAsync(candidate.file, candidate.args);
|
|
483
|
+
return;
|
|
484
|
+
} catch (error) {
|
|
485
|
+
lastError = error;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
throw lastError ?? new Error("no DNS cache flush command for this platform");
|
|
489
|
+
},
|
|
490
|
+
freePort: async (port, keepPid) => {
|
|
491
|
+
const pids = (0, import_node_os.platform)() === "win32" ? parseWindowsListeningPids((await execFileAsync("netstat", ["-ano"])).stdout, port) : parseListeningPids((await execFileQuiet("lsof", [`-ti:${port}`, "-sTCP:LISTEN"])).stdout);
|
|
492
|
+
const killed = [];
|
|
493
|
+
for (const pid of pids) {
|
|
494
|
+
if (pid === import_node_process.default.pid || keepPid !== void 0 && pid === keepPid)
|
|
495
|
+
continue;
|
|
496
|
+
try {
|
|
497
|
+
import_node_process.default.kill(pid, "SIGTERM");
|
|
498
|
+
killed.push(pid);
|
|
499
|
+
} catch {
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return killed;
|
|
503
|
+
},
|
|
504
|
+
caddyBin: options.caddyBin,
|
|
505
|
+
caddyConfigPath: options.caddyConfigPath,
|
|
506
|
+
caddyCwd: options.caddyCwd ?? import_node_process.default.cwd(),
|
|
507
|
+
caddyEnv: options.caddyEnv ?? {},
|
|
508
|
+
trustLocalCa: () => trustLocalCaWithSystemTools(options.caddyBin, options.caddyAdminSocket),
|
|
509
|
+
caddyStoragePath: options.caddyStoragePath,
|
|
510
|
+
storageOwner: options.storageOwner,
|
|
511
|
+
startCaddy: (emit) => {
|
|
512
|
+
var _a, _b;
|
|
513
|
+
const child = (0, import_node_child_process.spawn)(options.caddyBin, ["run", "--adapter", "caddyfile", "--config", options.caddyConfigPath, "--watch"], {
|
|
514
|
+
cwd: options.caddyCwd ?? import_node_process.default.cwd(),
|
|
515
|
+
env: { ...import_node_process.default.env, ...options.caddyEnv },
|
|
516
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
517
|
+
detached: (0, import_node_os.platform)() !== "win32"
|
|
518
|
+
});
|
|
519
|
+
const handle = {
|
|
520
|
+
pid: child.pid,
|
|
521
|
+
kill: (signal) => child.kill(signal),
|
|
522
|
+
running: true
|
|
523
|
+
};
|
|
524
|
+
(_a = child.stdout) == null ? void 0 : _a.on("data", (data) => emit({ type: "event", kind: "caddy.stdout", data: data.toString() }));
|
|
525
|
+
(_b = child.stderr) == null ? void 0 : _b.on("data", (data) => emit({ type: "event", kind: "caddy.stderr", data: data.toString() }));
|
|
526
|
+
child.on("exit", (code, signal) => {
|
|
527
|
+
handle.running = false;
|
|
528
|
+
emit({ type: "event", kind: "caddy.exit", code, signal });
|
|
529
|
+
});
|
|
530
|
+
caddy = handle;
|
|
531
|
+
return handle;
|
|
532
|
+
},
|
|
533
|
+
stopCaddy: async () => {
|
|
534
|
+
const current = caddy;
|
|
535
|
+
caddy = void 0;
|
|
536
|
+
if (!current)
|
|
537
|
+
return;
|
|
538
|
+
try {
|
|
539
|
+
current.kill("SIGTERM");
|
|
540
|
+
} catch {
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
log
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function attachSocket(socket, runtime, log) {
|
|
547
|
+
let buffer = "";
|
|
548
|
+
runtime.attach(socket);
|
|
549
|
+
socket.on("data", (chunk) => {
|
|
550
|
+
buffer += chunk.toString("utf8");
|
|
551
|
+
let index = buffer.indexOf("\n");
|
|
552
|
+
while (index >= 0) {
|
|
553
|
+
const line = buffer.slice(0, index);
|
|
554
|
+
buffer = buffer.slice(index + 1);
|
|
555
|
+
index = buffer.indexOf("\n");
|
|
556
|
+
if (!line.trim())
|
|
557
|
+
continue;
|
|
558
|
+
let request;
|
|
559
|
+
try {
|
|
560
|
+
request = JSON.parse(line);
|
|
561
|
+
} catch (error) {
|
|
562
|
+
log(`invalid request: ${error.message}`);
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
void runtime.handle(request, socket).then((response) => {
|
|
566
|
+
if (!socket.destroyed)
|
|
567
|
+
socket.write(serializeMessage(response));
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
socket.on("error", (error) => {
|
|
572
|
+
log(`connection error: ${error.message}`);
|
|
573
|
+
});
|
|
574
|
+
socket.on("close", () => {
|
|
575
|
+
void runtime.detach(socket);
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// src/privileged/bootstrap.ts
|
|
580
|
+
var HANDSHAKE_TIMEOUT_MS = 12e4;
|
|
581
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 3e4;
|
|
582
|
+
async function startHelper(options) {
|
|
583
|
+
const { socketPath, token } = options;
|
|
584
|
+
const log = options.log ?? ((message) => import_node_process2.default.stdout.write(`[uhrp-helper] ${message}
|
|
585
|
+
`));
|
|
586
|
+
for (const check of [
|
|
587
|
+
checkPrivilegedPath(options.caddyBin, { label: "Caddy binary", ownerUid: options.ownerUid }),
|
|
588
|
+
checkPrivilegedPath(options.caddyConfigPath, { label: "Caddyfile", ownerUid: options.ownerUid })
|
|
589
|
+
]) {
|
|
590
|
+
if (check) {
|
|
591
|
+
log(describeRejection(check));
|
|
592
|
+
throw new Error(describeRejection(check));
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
const deps = options.deps ?? createNodeHelperDeps({
|
|
596
|
+
hostsPath: options.hostsPath,
|
|
597
|
+
log,
|
|
598
|
+
caddyBin: options.caddyBin,
|
|
599
|
+
caddyConfigPath: options.caddyConfigPath,
|
|
600
|
+
caddyAdminSocket: options.caddyAdminSocket,
|
|
601
|
+
caddyCwd: options.caddyCwd,
|
|
602
|
+
caddyEnv: options.caddyEnv,
|
|
603
|
+
caddyStoragePath: options.caddyStoragePath,
|
|
604
|
+
storageOwner: typeof options.ownerUid === "number" ? { uid: options.ownerUid, gid: options.ownerGid ?? options.ownerUid } : void 0
|
|
605
|
+
});
|
|
606
|
+
const runtime = createHelperRuntime(deps);
|
|
607
|
+
await runtime.sweepStaleEntries().catch(() => {
|
|
608
|
+
});
|
|
609
|
+
try {
|
|
610
|
+
(0, import_node_fs3.rmSync)(socketPath, { force: true });
|
|
611
|
+
} catch {
|
|
612
|
+
}
|
|
613
|
+
const connections = /* @__PURE__ */ new Set();
|
|
614
|
+
const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
615
|
+
const exitOnIdle = options.exitOnIdle ?? true;
|
|
616
|
+
let idleTimer;
|
|
617
|
+
const stopIdleTimer = () => {
|
|
618
|
+
if (idleTimer) {
|
|
619
|
+
clearTimeout(idleTimer);
|
|
620
|
+
idleTimer = void 0;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
const server = (0, import_node_net3.createServer)((socket) => {
|
|
624
|
+
var _a;
|
|
625
|
+
let buffer = "";
|
|
626
|
+
let authenticated = false;
|
|
627
|
+
stopIdleTimer();
|
|
628
|
+
connections.add(socket);
|
|
629
|
+
socket.on("close", () => {
|
|
630
|
+
connections.delete(socket);
|
|
631
|
+
if (connections.size === 0)
|
|
632
|
+
scheduleIdleExit();
|
|
633
|
+
});
|
|
634
|
+
const timer = setTimeout(() => {
|
|
635
|
+
if (!authenticated)
|
|
636
|
+
socket.destroy();
|
|
637
|
+
}, HANDSHAKE_TIMEOUT_MS);
|
|
638
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
639
|
+
const onData = (chunk) => {
|
|
640
|
+
buffer += chunk.toString("utf8");
|
|
641
|
+
const index = buffer.indexOf("\n");
|
|
642
|
+
if (index < 0)
|
|
643
|
+
return;
|
|
644
|
+
const line = buffer.slice(0, index);
|
|
645
|
+
const rest = buffer.slice(index + 1);
|
|
646
|
+
let parsed;
|
|
647
|
+
try {
|
|
648
|
+
parsed = JSON.parse(line);
|
|
649
|
+
} catch {
|
|
650
|
+
socket.destroy();
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (parsed.type !== "handshake" || typeof parsed.token !== "string") {
|
|
654
|
+
socket.destroy();
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
clearTimeout(timer);
|
|
658
|
+
socket.removeListener("data", onData);
|
|
659
|
+
if (parsed.token !== token) {
|
|
660
|
+
socket.end(`${JSON.stringify({ type: "handshake", ok: false, reason: "invalid token" })}
|
|
661
|
+
`);
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
authenticated = true;
|
|
665
|
+
socket.write(`${JSON.stringify({ type: "handshake", ok: true, pid: import_node_process2.default.pid })}
|
|
666
|
+
`);
|
|
667
|
+
attachSocket(socket, runtime, log);
|
|
668
|
+
if (rest.trim() !== "")
|
|
669
|
+
socket.emit("data", import_node_buffer.Buffer.from(rest));
|
|
670
|
+
};
|
|
671
|
+
socket.on("data", onData);
|
|
672
|
+
socket.on("error", () => {
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
function scheduleIdleExit() {
|
|
676
|
+
var _a;
|
|
677
|
+
if (idleTimer || connections.size > 0)
|
|
678
|
+
return;
|
|
679
|
+
idleTimer = setTimeout(() => {
|
|
680
|
+
var _a2, _b;
|
|
681
|
+
idleTimer = void 0;
|
|
682
|
+
if (connections.size > 0)
|
|
683
|
+
return;
|
|
684
|
+
log("no clients left; shutting down");
|
|
685
|
+
removeOwnSocket(socketPath, token);
|
|
686
|
+
if (!exitOnIdle) {
|
|
687
|
+
server.close();
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
server.close(() => import_node_process2.default.exit(0));
|
|
691
|
+
(_b = (_a2 = setTimeout(() => import_node_process2.default.exit(0), 1e3)).unref) == null ? void 0 : _b.call(_a2);
|
|
692
|
+
}, idleTimeoutMs);
|
|
693
|
+
(_a = idleTimer.unref) == null ? void 0 : _a.call(idleTimer);
|
|
694
|
+
}
|
|
695
|
+
await new Promise((resolve, reject) => {
|
|
696
|
+
server.once("error", reject);
|
|
697
|
+
server.listen(socketPath, () => resolve());
|
|
698
|
+
});
|
|
699
|
+
scheduleIdleExit();
|
|
700
|
+
try {
|
|
701
|
+
if (typeof options.ownerUid === "number") {
|
|
702
|
+
(0, import_node_fs3.chownSync)(socketPath, options.ownerUid, options.ownerGid ?? options.ownerUid);
|
|
703
|
+
(0, import_node_fs3.chmodSync)(socketPath, 384);
|
|
704
|
+
} else {
|
|
705
|
+
(0, import_node_fs3.chmodSync)(socketPath, 384);
|
|
706
|
+
}
|
|
707
|
+
} catch (error) {
|
|
708
|
+
log(`could not set socket ownership: ${error.message}`);
|
|
709
|
+
}
|
|
710
|
+
return {
|
|
711
|
+
runtime,
|
|
712
|
+
async close() {
|
|
713
|
+
for (const socket of connections)
|
|
714
|
+
socket.destroy();
|
|
715
|
+
connections.clear();
|
|
716
|
+
await new Promise((resolve) => {
|
|
717
|
+
server.close(() => resolve());
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function removeOwnSocket(socketPath, token) {
|
|
723
|
+
if (import_node_process2.default.platform === "win32")
|
|
724
|
+
return;
|
|
725
|
+
try {
|
|
726
|
+
const tokensDir = (0, import_node_path2.dirname)(socketPath);
|
|
727
|
+
const tokenFile = (0, import_node_path2.join)(tokensDir, "helper.token");
|
|
728
|
+
const current = (0, import_node_fs3.existsSync)(tokenFile) ? (0, import_node_fs3.readFileSync)(tokenFile, "utf8").trim() : void 0;
|
|
729
|
+
if (current === token)
|
|
730
|
+
(0, import_node_fs3.rmSync)(tokenFile, { force: true });
|
|
731
|
+
} catch {
|
|
732
|
+
}
|
|
733
|
+
try {
|
|
734
|
+
(0, import_node_fs3.rmSync)(socketPath, { force: true });
|
|
735
|
+
} catch {
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// src/privileged-helper.ts
|
|
740
|
+
function parseArgs(argv) {
|
|
741
|
+
let socket = "";
|
|
742
|
+
let token = "";
|
|
743
|
+
let hosts;
|
|
744
|
+
let caddyBin = "";
|
|
745
|
+
let caddyConfig = "";
|
|
746
|
+
let ownerUid;
|
|
747
|
+
let ownerGid;
|
|
748
|
+
let caddyStorage;
|
|
749
|
+
let caddyAdmin;
|
|
750
|
+
for (let index = 0; index < argv.length; index++) {
|
|
751
|
+
const current = argv[index];
|
|
752
|
+
if (current === "--socket")
|
|
753
|
+
socket = argv[++index] ?? "";
|
|
754
|
+
else if (current === "--token")
|
|
755
|
+
token = argv[++index] ?? "";
|
|
756
|
+
else if (current === "--hosts")
|
|
757
|
+
hosts = argv[++index] ?? void 0;
|
|
758
|
+
else if (current === "--caddy")
|
|
759
|
+
caddyBin = argv[++index] ?? "";
|
|
760
|
+
else if (current === "--caddy-config")
|
|
761
|
+
caddyConfig = argv[++index] ?? "";
|
|
762
|
+
else if (current === "--owner-uid")
|
|
763
|
+
ownerUid = Number(argv[++index]);
|
|
764
|
+
else if (current === "--owner-gid")
|
|
765
|
+
ownerGid = Number(argv[++index]);
|
|
766
|
+
else if (current === "--caddy-storage")
|
|
767
|
+
caddyStorage = argv[++index] ?? void 0;
|
|
768
|
+
else if (current === "--caddy-admin")
|
|
769
|
+
caddyAdmin = argv[++index] ?? void 0;
|
|
770
|
+
}
|
|
771
|
+
if (!socket || !token)
|
|
772
|
+
throw new Error("usage: privileged-helper --socket <path> --token <token> --caddy <bin> --caddy-config <path>");
|
|
773
|
+
if (!caddyBin || !caddyConfig)
|
|
774
|
+
throw new Error("privileged-helper requires --caddy and --caddy-config");
|
|
775
|
+
return {
|
|
776
|
+
socket,
|
|
777
|
+
token,
|
|
778
|
+
hosts,
|
|
779
|
+
caddyBin,
|
|
780
|
+
caddyConfig,
|
|
781
|
+
ownerUid: Number.isInteger(ownerUid) ? ownerUid : void 0,
|
|
782
|
+
ownerGid: Number.isInteger(ownerGid) ? ownerGid : void 0,
|
|
783
|
+
caddyStorage,
|
|
784
|
+
caddyAdmin
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
async function main() {
|
|
788
|
+
const args = parseArgs(import_node_process3.default.argv.slice(2));
|
|
789
|
+
const helper = await startHelper({
|
|
790
|
+
socketPath: args.socket,
|
|
791
|
+
token: args.token,
|
|
792
|
+
hostsPath: args.hosts,
|
|
793
|
+
caddyBin: args.caddyBin,
|
|
794
|
+
caddyConfigPath: args.caddyConfig,
|
|
795
|
+
ownerUid: args.ownerUid,
|
|
796
|
+
ownerGid: args.ownerGid,
|
|
797
|
+
caddyStoragePath: args.caddyStorage,
|
|
798
|
+
caddyAdminSocket: args.caddyAdmin,
|
|
799
|
+
caddyEnv: {
|
|
800
|
+
HOME: import_node_process3.default.env.HOME ?? "",
|
|
801
|
+
// Keep Caddy's storage next to the invoking user's home even though the
|
|
802
|
+
// process runs as root.
|
|
803
|
+
XDG_DATA_HOME: import_node_process3.default.env.XDG_DATA_HOME ?? ""
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
for (const signal of ["SIGINT", "SIGQUIT", "SIGTSTP", "SIGHUP"])
|
|
807
|
+
import_node_process3.default.on(signal, () => {
|
|
808
|
+
});
|
|
809
|
+
import_node_process3.default.on("SIGTERM", () => {
|
|
810
|
+
if (helper.runtime.clientCount() > 0)
|
|
811
|
+
return;
|
|
812
|
+
void helper.runtime.dispose().finally(() => import_node_process3.default.exit(0));
|
|
813
|
+
});
|
|
814
|
+
import_node_process3.default.stdout.on("error", () => {
|
|
815
|
+
});
|
|
816
|
+
import_node_process3.default.stderr.on("error", () => {
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
main().catch((error) => {
|
|
820
|
+
import_node_process3.default.stderr.write(`[uhrp-helper] fatal: ${error.message}
|
|
821
|
+
`);
|
|
822
|
+
import_node_process3.default.exit(1);
|
|
823
|
+
});
|
|
824
|
+
exports.default = module.exports;
|