import type { ServerWebSocket } from 'bun' export interface WsData { path: string protocols: string[] } const GO_PORT = 8000 const GO_BASE = `http://127.0.0.1:${GO_PORT}` const upstreams = new Map, WebSocket>() export function createProxy(isHealthy: () => boolean, isRunning: () => boolean) { async function proxyFetch(req: Request): Promise { const url = new URL(req.url) if (url.pathname === '/ok') { if (!isHealthy()) return new Response('starting', { status: isRunning() ? 200 : 503 }) return fetch(`${GO_BASE}/health`) .then((r) => (r.ok ? new Response('ok') : new Response('unhealthy', { status: 503 }))) .catch(() => new Response('unhealthy', { status: 503 })) } const hasBody = req.method !== 'GET' && req.method !== 'HEAD' const body = hasBody ? await req.arrayBuffer() : undefined return fetch(`${GO_BASE}${url.pathname}${url.search}`, { method: req.method, headers: req.headers, body, redirect: 'manual', }).then((r) => { // Bun auto-decompresses gzip but leaves content-encoding header. // Strip it so the next proxy layer doesn't try to decompress again. const headers = new Headers(r.headers) headers.delete('content-encoding') headers.delete('content-length') return new Response(r.body, { status: r.status, headers }) }).catch((e) => { console.error('Proxy error:', e) return new Response('Tronbyt server is not responding', { status: 502 }) }) } const websocket = { open(ws: ServerWebSocket) { const upstream = new WebSocket(`ws://127.0.0.1:${GO_PORT}${ws.data.path}`, ws.data.protocols) upstream.binaryType = 'arraybuffer' upstreams.set(ws, upstream) upstream.addEventListener('message', (e) => ws.send(e.data as string | ArrayBuffer)) upstream.addEventListener('close', () => { upstreams.delete(ws); ws.close() }) upstream.addEventListener('error', () => { upstreams.delete(ws); ws.close() }) }, message(ws: ServerWebSocket, msg: string | ArrayBuffer | Uint8Array) { const upstream = upstreams.get(ws) if (upstream?.readyState === WebSocket.OPEN) upstream.send(msg) }, close(ws: ServerWebSocket) { upstreams.get(ws)?.close() upstreams.delete(ws) }, } return { proxyFetch, websocket } }