Files
pikasTech-HWLAB/internal/cloud/bun-server.ts
T
2026-06-05 19:26:13 +08:00

73 lines
2.8 KiB
TypeScript

import { createCloudApiServer } from "./server.ts";
import { createHwpodNodeWsRegistry } from "./hwpod-node-ws-registry.ts";
export async function createCloudApiBunServer(options: any = {}) {
const env = options.env ?? process.env;
const hwpodNodeWsRegistry = options.hwpodNodeWsRegistry || createHwpodNodeWsRegistry({ ...options, env });
const innerServer = createCloudApiServer({ ...options, env, hwpodNodeWsRegistry });
await new Promise((resolve) => innerServer.listen(0, "127.0.0.1", resolve));
const innerAddress = innerServer.address();
const innerPort = typeof innerAddress === "object" && innerAddress ? innerAddress.port : 0;
if (!innerPort) throw new Error("cloud-api inner HTTP server did not expose a port");
const host = options.host ?? "0.0.0.0";
const port = options.port ?? 0;
const server = Bun.serve({
hostname: host,
port,
async fetch(request, bunServer) {
const url = new URL(request.url);
if (url.pathname === "/v1/hwpod-node/ws") {
const expectedToken = String(env.HWLAB_HWPOD_NODE_WS_TOKEN ?? "").trim();
const token = String(url.searchParams.get("token") ?? request.headers.get("x-hwpod-node-token") ?? "");
if (expectedToken && token !== expectedToken) return json({ ok: false, error: { code: "hwpod_node_ws_token_invalid" } }, 401);
const upgraded = bunServer.upgrade(request, { data: { hwpodNodeWsRegistry } });
return upgraded ? undefined : json({ ok: false, status: "upgrade_required", route: "/v1/hwpod-node/ws" }, 426);
}
return proxyHttpToInnerServer(request, innerPort);
},
websocket: {
open(socket: any) {
socket.data.hwpodNodeWsRegistry.openBunSocket(socket);
},
message(socket: any, message: string | Buffer) {
socket.data.hwpodNodeWsRegistry.handleBunMessage(socket, message);
},
close(socket: any) {
socket.data.hwpodNodeWsRegistry.closeBunSocket(socket);
}
}
});
return {
server,
innerServer,
hwpodNodeWsRegistry,
port: server.port,
url: `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${server.port}`,
stop() {
server.stop(true);
innerServer.close();
}
};
}
async function proxyHttpToInnerServer(request: Request, innerPort: number) {
const sourceUrl = new URL(request.url);
const targetUrl = new URL(sourceUrl.pathname + sourceUrl.search, `http://127.0.0.1:${innerPort}`);
const method = request.method.toUpperCase();
return fetch(targetUrl, {
method,
headers: request.headers,
body: method === "GET" || method === "HEAD" ? undefined : request.body,
redirect: "manual"
});
}
function json(body: any, status = 200) {
return new Response(`${JSON.stringify(body)}\n`, {
status,
headers: { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" }
});
}