Spaces:
Running
Running
| ; | |
| var __importDefault = (this && this.__importDefault) || function (mod) { | |
| return (mod && mod.__esModule) ? mod : { "default": mod }; | |
| }; | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| exports.attachBridge = attachBridge; | |
| const ws_1 = require("ws"); | |
| const axios_1 = __importDefault(require("axios")); | |
| const form_data_1 = __importDefault(require("form-data")); | |
| const msgpack_1 = require("@msgpack/msgpack"); | |
| function normalizeHeaders(h) { | |
| const out = {}; | |
| if (!h || typeof h !== "object") | |
| return out; | |
| for (const k of Object.keys(h)) { | |
| const v = h[k]; | |
| if (v == null) | |
| continue; | |
| out[k.toLowerCase()] = Array.isArray(v) ? v.join(", ") : String(v); | |
| } | |
| return out; | |
| } | |
| function buildForm(body, files) { | |
| const form = new form_data_1.default(); | |
| if (body && typeof body === "object") { | |
| for (const [k, v] of Object.entries(body)) { | |
| if (v == null) | |
| continue; | |
| form.append(k, typeof v === "string" ? v : String(v)); | |
| } | |
| } | |
| for (const f of files) { | |
| if (!f || !f.field || !f.bytes) | |
| continue; | |
| const buf = Buffer.from(f.bytes); | |
| form.append(f.field, buf, { filename: f.name || "file", contentType: f.type || "application/octet-stream" }); | |
| } | |
| return form; | |
| } | |
| function attachBridge(server, port) { | |
| // 32 MiB cap covers a 20 MiB upload + envelope overhead with msgpack (no base64). | |
| const wss = new ws_1.WebSocketServer({ noServer: true, maxPayload: 32 * 1024 * 1024 }); | |
| server.on("upgrade", (req, socket, head) => { | |
| const { pathname } = new URL(req.url || "/", `http://${req.headers.host}`); | |
| if (pathname === "/ws/bridge") { | |
| wss.handleUpgrade(req, socket, head, (ws) => wss.emit("connection", ws, req)); | |
| } | |
| }); | |
| wss.on("connection", (ws) => { | |
| let alive = true; | |
| const ping = setInterval(() => { | |
| if (!alive) { | |
| try { | |
| ws.terminate(); | |
| } | |
| catch { } | |
| return; | |
| } | |
| alive = false; | |
| try { | |
| ws.ping(); | |
| } | |
| catch { } | |
| }, 30_000); | |
| ws.on("pong", () => { alive = true; }); | |
| ws.on("close", () => clearInterval(ping)); | |
| ws.on("message", async (raw) => { | |
| let m; | |
| try { | |
| const buf = Array.isArray(raw) | |
| ? Buffer.concat(raw) | |
| : raw instanceof ArrayBuffer ? Buffer.from(raw) : raw; | |
| m = (0, msgpack_1.decode)(buf); | |
| } | |
| catch { | |
| return; | |
| } | |
| if (!m || !m.id || !m.method || !m.path) | |
| return; | |
| const url = `http://localhost:${port}${m.path}`; | |
| const cfg = { | |
| params: m.params, | |
| timeout: 30_000, | |
| validateStatus: () => true, | |
| maxBodyLength: Infinity, | |
| maxContentLength: Infinity, | |
| }; | |
| if (m.followRedirects === false) | |
| cfg.maxRedirects = 0; | |
| let res; | |
| try { | |
| let r; | |
| if (m.method === "GET") { | |
| r = await axios_1.default.get(url, cfg); | |
| } | |
| else if (m.files && m.files.length > 0) { | |
| const form = buildForm(m.body, m.files); | |
| r = await axios_1.default.post(url, form, { ...cfg, headers: form.getHeaders() }); | |
| } | |
| else { | |
| r = await axios_1.default.post(url, m.body, cfg); | |
| } | |
| res = { | |
| id: m.id, | |
| ok: r.status >= 200 && r.status < 300, | |
| status: r.status, | |
| data: r.data, | |
| headers: normalizeHeaders(r.headers), | |
| }; | |
| } | |
| catch (e) { | |
| const r = e?.response; | |
| if (r && typeof r.status === "number") { | |
| res = { | |
| id: m.id, | |
| ok: r.status >= 200 && r.status < 300, | |
| status: r.status, | |
| data: r.data, | |
| headers: normalizeHeaders(r.headers), | |
| }; | |
| } | |
| else { | |
| res = { id: m.id, ok: false, status: 0, data: { message: e?.message || "bridge error" } }; | |
| } | |
| } | |
| try { | |
| ws.send((0, msgpack_1.encode)(res)); | |
| } | |
| catch { } | |
| }); | |
| }); | |
| console.log(`🛰️ Bridge WS attached at /ws/bridge`); | |
| } | |