@kvbox/server 0.5.13 → 0.5.14
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/dist/kvbox-server.js +269 -22
- package/dist/kvbox-server.js.map +1 -1
- package/package.json +1 -1
package/dist/kvbox-server.js
CHANGED
|
@@ -351,6 +351,18 @@ var PortForwardSchema = z3.object({
|
|
|
351
351
|
var PortsRequest = z3.object({
|
|
352
352
|
ports: z3.array(z3.object({ guestPort: z3.number().int().min(1).max(65535), proto: PortProtoSchema.optional() })).min(1).max(MAX_BOX_PORTS)
|
|
353
353
|
});
|
|
354
|
+
var BoxPortSchema = z3.number().int().min(1).max(65535);
|
|
355
|
+
var ClaimSubdomainRequest = z3.object({ name: z3.string(), port: BoxPortSchema });
|
|
356
|
+
var SubdomainPortRequest = z3.object({ port: BoxPortSchema });
|
|
357
|
+
var SubdomainRouteRequest = z3.object({ name: z3.string(), port: BoxPortSchema });
|
|
358
|
+
var SubdomainSchema = z3.object({
|
|
359
|
+
owner: z3.string(),
|
|
360
|
+
box: z3.string(),
|
|
361
|
+
subdomain: z3.string(),
|
|
362
|
+
port: BoxPortSchema,
|
|
363
|
+
url: z3.string(),
|
|
364
|
+
routes: z3.array(z3.object({ name: z3.string(), port: BoxPortSchema, url: z3.string() }))
|
|
365
|
+
});
|
|
354
366
|
var MachineSchema = z3.object({
|
|
355
367
|
/** Host-unique short id (the tap is `kv-<id>`). Missing only on a box made before ids that has not restarted since. */
|
|
356
368
|
id: z3.string(),
|
|
@@ -1970,7 +1982,7 @@ async function restoreMachine(fromText, owner, req, cfg, p) {
|
|
|
1970
1982
|
}
|
|
1971
1983
|
}
|
|
1972
1984
|
var AGENT_REPLY_MS = 2e4;
|
|
1973
|
-
async function introduce(spec, vsock, cfg,
|
|
1985
|
+
async function introduce(spec, vsock, cfg, failure2) {
|
|
1974
1986
|
const id = { name: spec.name, ip: spec.ip, prefixLen: prefixLenOf(cfg.subnet), gateway: cfg.gateway, mac: spec.mac, time: Math.floor(Date.now() / 1e3) };
|
|
1975
1987
|
const deadline = Date.now() + 3e3;
|
|
1976
1988
|
for (; ; ) {
|
|
@@ -1985,9 +1997,9 @@ async function introduce(spec, vsock, cfg, failure) {
|
|
|
1985
1997
|
continue;
|
|
1986
1998
|
}
|
|
1987
1999
|
if (transient) throw new KvboxError(`${msg}
|
|
1988
|
-
${
|
|
2000
|
+
${failure2.silent}`);
|
|
1989
2001
|
throw new KvboxError(`${msg}
|
|
1990
|
-
${
|
|
2002
|
+
${failure2.noAnswer}`);
|
|
1991
2003
|
}
|
|
1992
2004
|
}
|
|
1993
2005
|
}
|
|
@@ -2658,7 +2670,7 @@ import { createServer as createHttpsServer } from "node:https";
|
|
|
2658
2670
|
import { createAdaptorServer } from "@hono/node-server";
|
|
2659
2671
|
|
|
2660
2672
|
// src/app.ts
|
|
2661
|
-
import { Hono as
|
|
2673
|
+
import { Hono as Hono7 } from "hono";
|
|
2662
2674
|
|
|
2663
2675
|
// ../shared/src/version.ts
|
|
2664
2676
|
import { createRequire } from "node:module";
|
|
@@ -2673,10 +2685,25 @@ function versionOf(srcFileUrl, injected) {
|
|
|
2673
2685
|
}
|
|
2674
2686
|
|
|
2675
2687
|
// src/version.ts
|
|
2676
|
-
var VERSION = versionOf(import.meta.url, true ? "0.5.
|
|
2688
|
+
var VERSION = versionOf(import.meta.url, true ? "0.5.14" : void 0);
|
|
2677
2689
|
|
|
2678
2690
|
// src/caller.ts
|
|
2679
2691
|
import { createHash as createHash2, timingSafeEqual } from "node:crypto";
|
|
2692
|
+
|
|
2693
|
+
// ../shared/src/names.ts
|
|
2694
|
+
import { createHmac } from "node:crypto";
|
|
2695
|
+
var PROXY_ERROR_HEADER = "x-kvbox-proxy-error";
|
|
2696
|
+
var PROXIED_HEADER = "x-kvbox-proxied";
|
|
2697
|
+
var VISITOR_PATH_HEADER = "x-kvbox-path";
|
|
2698
|
+
var VISITOR_AUTH_HEADER = "x-kvbox-authorization";
|
|
2699
|
+
var NOT_FORWARDED = /* @__PURE__ */ new Set(["expect"]);
|
|
2700
|
+
var HOP_BY_HOP = /* @__PURE__ */ new Set(["connection", "keep-alive", "proxy-authenticate", "proxy-authorization", "proxy-connection", "te", "trailer", "transfer-encoding", "upgrade"]);
|
|
2701
|
+
var BOX_HTTP_PATH_RE = /^\/machines\/([^/]+)\/http\/(\d{1,5})\/?$/;
|
|
2702
|
+
function edgeCredential(hostSecret) {
|
|
2703
|
+
return createHmac("sha256", hostSecret).update("kvbox-edge-v1").digest("base64url");
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
// src/caller.ts
|
|
2680
2707
|
var OWNER_HEADER = "x-kvbox-owner";
|
|
2681
2708
|
function resolveCaller(opts = {}) {
|
|
2682
2709
|
const cp = opts.controlPlane;
|
|
@@ -2688,10 +2715,11 @@ function resolveCaller(opts = {}) {
|
|
|
2688
2715
|
await next();
|
|
2689
2716
|
};
|
|
2690
2717
|
}
|
|
2691
|
-
const
|
|
2718
|
+
const authorized = secretCheck(cp);
|
|
2719
|
+
const edge = edgeCheck(cp);
|
|
2692
2720
|
return async (c, next) => {
|
|
2693
|
-
const
|
|
2694
|
-
if (!
|
|
2721
|
+
const auth = c.req.header("authorization");
|
|
2722
|
+
if (!authorized(auth) && !(edge(auth) && BOX_HTTP_PATH_RE.test(c.req.path))) return c.json({ error: "not authorized" }, 401);
|
|
2695
2723
|
const user = c.req.header(OWNER_HEADER);
|
|
2696
2724
|
if (user === void 0) {
|
|
2697
2725
|
const path = c.req.path.replace(/\/$/, "");
|
|
@@ -2710,20 +2738,237 @@ function resolveCaller(opts = {}) {
|
|
|
2710
2738
|
await next();
|
|
2711
2739
|
};
|
|
2712
2740
|
}
|
|
2741
|
+
function secretCheck(cp) {
|
|
2742
|
+
return bearerCheck(cp.secret);
|
|
2743
|
+
}
|
|
2744
|
+
function edgeCheck(cp) {
|
|
2745
|
+
return bearerCheck(edgeCredential(cp.secret));
|
|
2746
|
+
}
|
|
2747
|
+
function bearerCheck(token) {
|
|
2748
|
+
const expected = digest(token);
|
|
2749
|
+
return (authorization) => {
|
|
2750
|
+
const m = /^Bearer\s+(\S+)$/i.exec(authorization ?? "");
|
|
2751
|
+
return m !== null && timingSafeEqual(digest(m[1]), expected);
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2713
2754
|
function digest(secret) {
|
|
2714
2755
|
return createHash2("sha256").update(secret).digest();
|
|
2715
2756
|
}
|
|
2716
2757
|
|
|
2717
|
-
// src/routes/
|
|
2758
|
+
// src/routes/boxHttp.ts
|
|
2759
|
+
import { connect as connect2 } from "node:net";
|
|
2760
|
+
import { Readable } from "node:stream";
|
|
2718
2761
|
import { Hono } from "hono";
|
|
2719
|
-
|
|
2762
|
+
import { request as request3 } from "undici";
|
|
2763
|
+
|
|
2764
|
+
// ../shared/src/httpHead.ts
|
|
2765
|
+
var END = "\r\n\r\n";
|
|
2766
|
+
var HeadTimeoutError = class extends Error {
|
|
2767
|
+
};
|
|
2768
|
+
function readHead(socket, opts) {
|
|
2769
|
+
return new Promise((resolve, reject) => {
|
|
2770
|
+
let bytes = Buffer.alloc(0);
|
|
2771
|
+
const done = (err) => {
|
|
2772
|
+
clearTimeout(timer);
|
|
2773
|
+
socket.off("data", onData);
|
|
2774
|
+
socket.off("close", onClose);
|
|
2775
|
+
socket.pause();
|
|
2776
|
+
if (err) return reject(err);
|
|
2777
|
+
const end = bytes.indexOf(END);
|
|
2778
|
+
resolve({ head: bytes.subarray(0, end).toString("latin1"), rest: bytes.subarray(end + END.length) });
|
|
2779
|
+
};
|
|
2780
|
+
const onData = (chunk) => {
|
|
2781
|
+
bytes = Buffer.concat([bytes, chunk]);
|
|
2782
|
+
if (bytes.includes(END)) done();
|
|
2783
|
+
else if (bytes.length > opts.maxBytes) done(new Error("answer head too large"));
|
|
2784
|
+
};
|
|
2785
|
+
const onClose = () => done(new Error("closed before answering"));
|
|
2786
|
+
const timer = setTimeout(() => done(new HeadTimeoutError(`no answer within ${opts.timeoutMs / 1e3} s`)), opts.timeoutMs);
|
|
2787
|
+
socket.on("data", onData);
|
|
2788
|
+
socket.once("close", onClose);
|
|
2789
|
+
});
|
|
2790
|
+
}
|
|
2791
|
+
function rewriteHead(head, drop, add = {}) {
|
|
2792
|
+
const [status, ...lines] = head.split("\r\n");
|
|
2793
|
+
const kept = lines.filter((l) => !drop(l.slice(0, l.indexOf(":")).trim().toLowerCase()));
|
|
2794
|
+
const extra = Object.entries(add).map(([k, v]) => `${k}: ${v}`);
|
|
2795
|
+
return Buffer.from(`${[status, ...kept, ...extra].join("\r\n")}${END}`, "latin1");
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
// src/routes/boxHttp.ts
|
|
2799
|
+
var HEADERS_TIMEOUT_MS = 55e3;
|
|
2800
|
+
var MAX_HANDSHAKE_BYTES = 16 * 1024;
|
|
2801
|
+
var CONNECT_TIMEOUT_MS = 5e3;
|
|
2802
|
+
var DROPPED = /* @__PURE__ */ new Set([...HOP_BY_HOP, ...NOT_FORWARDED, "host", "authorization", OWNER_HEADER, VISITOR_AUTH_HEADER, VISITOR_PATH_HEADER]);
|
|
2803
|
+
var ProxyFailure = class extends Error {
|
|
2804
|
+
constructor(status, code, message) {
|
|
2805
|
+
super(message);
|
|
2806
|
+
this.status = status;
|
|
2807
|
+
this.code = code;
|
|
2808
|
+
}
|
|
2809
|
+
status;
|
|
2810
|
+
code;
|
|
2811
|
+
};
|
|
2812
|
+
function visitorPath(value) {
|
|
2813
|
+
const path = Array.isArray(value) ? value[0] : value;
|
|
2814
|
+
return path !== void 0 && path !== null && path.startsWith("/") && !/[\s]/.test(path) ? path : "/";
|
|
2815
|
+
}
|
|
2816
|
+
async function targetOf(url, owner, path) {
|
|
2817
|
+
const m = BOX_HTTP_PATH_RE.exec(url.pathname);
|
|
2818
|
+
const port = Number(m?.[2]);
|
|
2819
|
+
if (!m || port < 1 || port > 65535) throw new ProxyFailure(404, "not-found", `no route: ${url.pathname}`);
|
|
2820
|
+
const name = decodeURIComponent(m[1]);
|
|
2821
|
+
let box;
|
|
2822
|
+
try {
|
|
2823
|
+
box = await getMachine(parseRef(name, owner));
|
|
2824
|
+
} catch (err) {
|
|
2825
|
+
if (err instanceof NotFoundError) throw new ProxyFailure(404, "not-found", err.message);
|
|
2826
|
+
throw err;
|
|
2827
|
+
}
|
|
2828
|
+
if (box.status !== "running") throw new ProxyFailure(409, "not-running", `box ${name} is ${box.status}`);
|
|
2829
|
+
return { ip: box.ip, port, path };
|
|
2830
|
+
}
|
|
2831
|
+
function boxHeaders(incoming, target, upgrade = false) {
|
|
2832
|
+
const out = [];
|
|
2833
|
+
let host = `${target.ip}:${target.port}`;
|
|
2834
|
+
for (const [name, value] of incoming) {
|
|
2835
|
+
const key = name.toLowerCase();
|
|
2836
|
+
if (key === "x-forwarded-host") host = value;
|
|
2837
|
+
if (key === VISITOR_AUTH_HEADER) out.push(["authorization", value]);
|
|
2838
|
+
if (upgrade && (key === "upgrade" || key === "connection")) out.push([name, value]);
|
|
2839
|
+
else if (!DROPPED.has(key)) out.push([name, value]);
|
|
2840
|
+
}
|
|
2841
|
+
out.push(["host", host]);
|
|
2842
|
+
return out;
|
|
2843
|
+
}
|
|
2844
|
+
var failure = (err) => new Response(JSON.stringify({ error: err.message }), { status: err.status, headers: { "content-type": "application/json", [PROXY_ERROR_HEADER]: err.code } });
|
|
2845
|
+
function boxHttpRoutes() {
|
|
2720
2846
|
const r = new Hono();
|
|
2847
|
+
r.all("/:name/http/:port", forward);
|
|
2848
|
+
return r;
|
|
2849
|
+
}
|
|
2850
|
+
async function forward(c) {
|
|
2851
|
+
let target;
|
|
2852
|
+
try {
|
|
2853
|
+
target = await targetOf(new URL(c.req.url), c.get("owner"), visitorPath(c.req.header(VISITOR_PATH_HEADER)));
|
|
2854
|
+
} catch (err) {
|
|
2855
|
+
if (err instanceof ProxyFailure) return failure(err);
|
|
2856
|
+
throw err;
|
|
2857
|
+
}
|
|
2858
|
+
const raw = c.req.raw;
|
|
2859
|
+
const pairs = [];
|
|
2860
|
+
raw.headers.forEach((value, name) => pairs.push([name, value]));
|
|
2861
|
+
let res;
|
|
2862
|
+
try {
|
|
2863
|
+
res = await request3(`http://${target.ip}:${target.port}${target.path}`, {
|
|
2864
|
+
method: raw.method,
|
|
2865
|
+
headers: boxHeaders(pairs, target).flat(),
|
|
2866
|
+
body: raw.body === null ? null : Readable.fromWeb(raw.body),
|
|
2867
|
+
headersTimeout: HEADERS_TIMEOUT_MS,
|
|
2868
|
+
bodyTimeout: 0,
|
|
2869
|
+
signal: raw.signal
|
|
2870
|
+
});
|
|
2871
|
+
} catch (err) {
|
|
2872
|
+
const e = err;
|
|
2873
|
+
if (e.code === "UND_ERR_HEADERS_TIMEOUT") return failure(new ProxyFailure(504, "timeout", `box took over ${HEADERS_TIMEOUT_MS / 1e3} s to answer on port ${target.port}`));
|
|
2874
|
+
return failure(new ProxyFailure(502, "unreachable", `box answered nothing on port ${target.port} (${e.code ?? e.message})`));
|
|
2875
|
+
}
|
|
2876
|
+
const headers = new Headers();
|
|
2877
|
+
for (const [name, value] of Object.entries(res.headers)) {
|
|
2878
|
+
if (value === void 0 || HOP_BY_HOP.has(name) || name.startsWith("x-kvbox-")) continue;
|
|
2879
|
+
for (const v of Array.isArray(value) ? value : [value]) headers.append(name, v);
|
|
2880
|
+
}
|
|
2881
|
+
headers.set(PROXIED_HEADER, "1");
|
|
2882
|
+
const empty = res.statusCode === 204 || res.statusCode === 304 || raw.method === "HEAD";
|
|
2883
|
+
if (empty) res.body.resume();
|
|
2884
|
+
return new Response(empty ? null : Readable.toWeb(res.body), { status: res.statusCode, headers });
|
|
2885
|
+
}
|
|
2886
|
+
function refuse(socket, status, text, code = "refused") {
|
|
2887
|
+
const body = JSON.stringify({ error: text });
|
|
2888
|
+
socket.end(`HTTP/1.1 ${status} ${status === 401 ? "Unauthorized" : "Error"}\r
|
|
2889
|
+
content-type: application/json\r
|
|
2890
|
+
content-length: ${Buffer.byteLength(body)}\r
|
|
2891
|
+
${PROXY_ERROR_HEADER}: ${code}\r
|
|
2892
|
+
connection: close\r
|
|
2893
|
+
\r
|
|
2894
|
+
${body}`);
|
|
2895
|
+
}
|
|
2896
|
+
function headerPairs(raw) {
|
|
2897
|
+
const out = [];
|
|
2898
|
+
for (let i = 0; i + 1 < raw.length; i += 2) out.push([raw[i], raw[i + 1]]);
|
|
2899
|
+
return out;
|
|
2900
|
+
}
|
|
2901
|
+
function boxUpgradeHandler(cp, headersTimeoutMs = HEADERS_TIMEOUT_MS) {
|
|
2902
|
+
const full = secretCheck(cp);
|
|
2903
|
+
const edge = edgeCheck(cp);
|
|
2904
|
+
const authorized = (auth) => full(auth) || edge(auth);
|
|
2905
|
+
return (req, socket, head) => {
|
|
2906
|
+
socket.on("error", () => socket.destroy());
|
|
2907
|
+
void (async () => {
|
|
2908
|
+
const headers = req.headers;
|
|
2909
|
+
if (!authorized(headers.authorization)) return refuse(socket, 401, "not authorized");
|
|
2910
|
+
const user = headers[OWNER_HEADER];
|
|
2911
|
+
if (typeof user !== "string" || !LOCAL_OWNER_RE.test(user)) return refuse(socket, 400, "X-Kvbox-Owner header required");
|
|
2912
|
+
let target;
|
|
2913
|
+
try {
|
|
2914
|
+
target = await targetOf(new URL(req.url ?? "/", "https://host"), `${user}@${cp.name}`, visitorPath(headers[VISITOR_PATH_HEADER]));
|
|
2915
|
+
} catch (err) {
|
|
2916
|
+
if (err instanceof ProxyFailure) return refuse(socket, err.status, err.message, err.code);
|
|
2917
|
+
return refuse(socket, 500, err.message);
|
|
2918
|
+
}
|
|
2919
|
+
if (socket.destroyed) return;
|
|
2920
|
+
const box = connect2({ host: target.ip, port: target.port, timeout: CONNECT_TIMEOUT_MS });
|
|
2921
|
+
let connected = false;
|
|
2922
|
+
let piped = false;
|
|
2923
|
+
box.once("timeout", () => box.destroy(new Error("timed out")));
|
|
2924
|
+
box.once("error", (err) => {
|
|
2925
|
+
if (!connected && !socket.destroyed) refuse(socket, 502, `box answered nothing on port ${target.port} (${err.code ?? err.message})`, "unreachable");
|
|
2926
|
+
else if (piped) socket.destroy();
|
|
2927
|
+
});
|
|
2928
|
+
socket.once("close", () => box.destroy());
|
|
2929
|
+
box.once("close", () => {
|
|
2930
|
+
if (piped) socket.destroy();
|
|
2931
|
+
});
|
|
2932
|
+
box.once("connect", () => {
|
|
2933
|
+
connected = true;
|
|
2934
|
+
box.setTimeout(0);
|
|
2935
|
+
const lines = boxHeaders(headerPairs(req.rawHeaders), target, true).map(([k, v]) => `${k}: ${v}\r
|
|
2936
|
+
`);
|
|
2937
|
+
box.write(`${req.method ?? "GET"} ${target.path} HTTP/1.1\r
|
|
2938
|
+
${lines.join("")}\r
|
|
2939
|
+
`);
|
|
2940
|
+
if (head.length > 0) box.write(head);
|
|
2941
|
+
readHead(box, { maxBytes: MAX_HANDSHAKE_BYTES, timeoutMs: headersTimeoutMs }).then(
|
|
2942
|
+
(answer) => {
|
|
2943
|
+
if (socket.destroyed) return void box.destroy();
|
|
2944
|
+
socket.write(rewriteHead(answer.head, (name) => name.startsWith("x-kvbox-"), { [PROXIED_HEADER]: "1" }));
|
|
2945
|
+
if (answer.rest.length > 0) socket.write(answer.rest);
|
|
2946
|
+
piped = true;
|
|
2947
|
+
box.pipe(socket);
|
|
2948
|
+
socket.pipe(box);
|
|
2949
|
+
},
|
|
2950
|
+
(err) => {
|
|
2951
|
+
box.destroy();
|
|
2952
|
+
if (socket.destroyed) return;
|
|
2953
|
+
if (err instanceof HeadTimeoutError) refuse(socket, 504, `box took over ${headersTimeoutMs / 1e3} s to answer on port ${target.port}`, "timeout");
|
|
2954
|
+
else refuse(socket, 502, `box gave no answer on port ${target.port} (${err.message})`, "unreachable");
|
|
2955
|
+
}
|
|
2956
|
+
);
|
|
2957
|
+
});
|
|
2958
|
+
})();
|
|
2959
|
+
};
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
// src/routes/capacity.ts
|
|
2963
|
+
import { Hono as Hono2 } from "hono";
|
|
2964
|
+
function capacityRoutes() {
|
|
2965
|
+
const r = new Hono2();
|
|
2721
2966
|
r.get("/", async (c) => c.json(await capacity()));
|
|
2722
2967
|
return r;
|
|
2723
2968
|
}
|
|
2724
2969
|
|
|
2725
2970
|
// src/routes/machines.ts
|
|
2726
|
-
import { Hono as
|
|
2971
|
+
import { Hono as Hono3 } from "hono";
|
|
2727
2972
|
|
|
2728
2973
|
// src/core/stats.ts
|
|
2729
2974
|
var STATS_REPLY_MS = 3e3;
|
|
@@ -2756,7 +3001,7 @@ async function usageReport(scope, window, p = paths()) {
|
|
|
2756
3001
|
|
|
2757
3002
|
// src/routes/machines.ts
|
|
2758
3003
|
function machineRoutes() {
|
|
2759
|
-
const r = new
|
|
3004
|
+
const r = new Hono3();
|
|
2760
3005
|
r.get("/", async (c) => c.json(await listMachines(c.get("listScope"))));
|
|
2761
3006
|
r.post("/", async (c) => {
|
|
2762
3007
|
const body = CreateMachineRequest.parse(await c.req.json().catch(() => ({})));
|
|
@@ -2812,7 +3057,7 @@ function machineRoutes() {
|
|
|
2812
3057
|
}
|
|
2813
3058
|
|
|
2814
3059
|
// src/routes/snapshots.ts
|
|
2815
|
-
import { Hono as
|
|
3060
|
+
import { Hono as Hono4 } from "hono";
|
|
2816
3061
|
|
|
2817
3062
|
// src/core/snapshots.ts
|
|
2818
3063
|
import { existsSync as existsSync3 } from "node:fs";
|
|
@@ -2882,21 +3127,21 @@ async function snapshotBox(ref, req, owner, p) {
|
|
|
2882
3127
|
try {
|
|
2883
3128
|
const since = box.memSnapshots ?? 0;
|
|
2884
3129
|
const mode = since === 0 || existsSync3(m.memBase) ? "Diff" : "Full";
|
|
2885
|
-
let
|
|
3130
|
+
let failure2;
|
|
2886
3131
|
await fc.pause();
|
|
2887
3132
|
try {
|
|
2888
3133
|
await fc.snapshotCreate(s.vmstate, s.mem, mode);
|
|
2889
3134
|
lineageTouched = true;
|
|
2890
3135
|
await run(["cp", "--sparse=always", "--reflink=auto", m.overlay, s.overlay]);
|
|
2891
3136
|
} catch (err) {
|
|
2892
|
-
|
|
3137
|
+
failure2 = err;
|
|
2893
3138
|
} finally {
|
|
2894
3139
|
if (!box.paused) await fc.resume();
|
|
2895
3140
|
}
|
|
2896
3141
|
if (lineageTouched) {
|
|
2897
3142
|
await writeMachineMeta({ ...box, memSnapshots: since + 1 }, p);
|
|
2898
3143
|
}
|
|
2899
|
-
if (
|
|
3144
|
+
if (failure2 !== void 0) throw failure2;
|
|
2900
3145
|
if (mode === "Full") {
|
|
2901
3146
|
await run(["fallocate", "--dig-holes", s.mem]);
|
|
2902
3147
|
} else if (since > 0) {
|
|
@@ -2939,7 +3184,7 @@ async function deleteSnapshot(ref, p = paths()) {
|
|
|
2939
3184
|
|
|
2940
3185
|
// src/routes/snapshots.ts
|
|
2941
3186
|
function snapshotRoutes() {
|
|
2942
|
-
const r = new
|
|
3187
|
+
const r = new Hono4();
|
|
2943
3188
|
r.get("/", async (c) => c.json(await listSnapshots(c.get("listScope"))));
|
|
2944
3189
|
r.post("/", async (c) => {
|
|
2945
3190
|
const body = CreateSnapshotRequest.parse(await c.req.json());
|
|
@@ -2953,9 +3198,9 @@ function snapshotRoutes() {
|
|
|
2953
3198
|
}
|
|
2954
3199
|
|
|
2955
3200
|
// src/routes/ssh.ts
|
|
2956
|
-
import { Hono as
|
|
3201
|
+
import { Hono as Hono5 } from "hono";
|
|
2957
3202
|
function sshRoutes(opts = {}) {
|
|
2958
|
-
const r = new
|
|
3203
|
+
const r = new Hono5();
|
|
2959
3204
|
if (opts.controlPlane) {
|
|
2960
3205
|
r.all("/*", (c) => c.json({ error: "trusting a CA is done on the host itself (kvbox server trust-ca there); the control plane cannot ask a host to" }, 403));
|
|
2961
3206
|
return r;
|
|
@@ -2970,10 +3215,10 @@ function sshRoutes(opts = {}) {
|
|
|
2970
3215
|
}
|
|
2971
3216
|
|
|
2972
3217
|
// src/routes/usage.ts
|
|
2973
|
-
import { Hono as
|
|
3218
|
+
import { Hono as Hono6 } from "hono";
|
|
2974
3219
|
var PULL_LIMIT = 1e3;
|
|
2975
3220
|
function usageRoutes(opts = {}) {
|
|
2976
|
-
const r = new
|
|
3221
|
+
const r = new Hono6();
|
|
2977
3222
|
r.get("/", async (c) => {
|
|
2978
3223
|
let window;
|
|
2979
3224
|
try {
|
|
@@ -3000,7 +3245,7 @@ function whole(value, name) {
|
|
|
3000
3245
|
|
|
3001
3246
|
// src/app.ts
|
|
3002
3247
|
function createApp(opts = {}) {
|
|
3003
|
-
const app = new
|
|
3248
|
+
const app = new Hono7();
|
|
3004
3249
|
app.use(resolveCaller(opts));
|
|
3005
3250
|
app.get("/health", async (c) => {
|
|
3006
3251
|
const cfg = await loadConfig();
|
|
@@ -3016,6 +3261,7 @@ function createApp(opts = {}) {
|
|
|
3016
3261
|
recovery: lastRecovery()
|
|
3017
3262
|
});
|
|
3018
3263
|
});
|
|
3264
|
+
if (opts.controlPlane) app.route("/machines", boxHttpRoutes());
|
|
3019
3265
|
app.route("/machines", machineRoutes());
|
|
3020
3266
|
app.route("/snapshots", snapshotRoutes());
|
|
3021
3267
|
app.route("/capacity", capacityRoutes());
|
|
@@ -3061,6 +3307,7 @@ async function listen(opts = {}) {
|
|
|
3061
3307
|
createServer: createHttpsServer,
|
|
3062
3308
|
serverOptions: { key, cert }
|
|
3063
3309
|
});
|
|
3310
|
+
httpsServer.on("upgrade", boxUpgradeHandler({ name: cp.name, secret: cp.secret }));
|
|
3064
3311
|
const port = opts.httpsPort ?? cp.port;
|
|
3065
3312
|
await new Promise((resolve, reject) => {
|
|
3066
3313
|
httpsServer.once("error", reject);
|