import nose from "./server" const SNEAKER_URL = "nose.space" const SNEAKER_TLS = true // const SNEAKER_URL = "localhost:3100" // const SNEAKER_TLS = false type Connection = { subdomain: string ws: any close: boolean // manual close } const conns: Record = {} export function sneakerUrl(appOrSubdomain: string): string { let conn = conns[appOrSubdomain] if (!conn) { for (const appName of Object.keys(conns)) { if (conns[appName]?.subdomain === appOrSubdomain) { conn = conns[appName] break } } if (!conn) return "none" } let url = "http" + (SNEAKER_TLS ? "s" : "") + "://" url += conn.subdomain + "." + SNEAKER_URL return url } export function sneakers(): string[] { return Object.keys(conns) } export function disconnectSneaker(app: string): boolean { if (!sneakers().includes(app) || !conns[app]) return false conns[app].close = true conns[app].ws.close() return true } // returns the sneaker subdomain if successful export async function connectSneaker(app: string, subdomain = ""): Promise { if (conns[app]) { return conns[app].subdomain } let url = `ws${SNEAKER_TLS ? "s" : ""}://${SNEAKER_URL}/tunnel?app=${app}` if (subdomain) url += `&subdomain=${subdomain}` const ws = new WebSocket(url) let resolve: (v: string) => void let promise = new Promise(res => resolve = res) ws.onclose = e => { if (!conns[app]?.close) setTimeout(() => connectSneaker(app, subdomain), 1000) // simple retry delete conns[app] } ws.onerror = e => console.error("sneaker error", e) ws.onmessage = async event => { const msg = JSON.parse(event.data.toString()) if (msg.subdomain) { conns[app] = { subdomain: msg.subdomain, ws, close: false } subdomain = msg.subdomain resolve(msg.subdomain) return } try { const req = new Request(`http://${msg.app}.localhost` + msg.path, { method: msg.method, headers: msg.headers, body: msg.body || undefined, }) const res = await nose.fetch(req) const body = await res.text() const headers: Record = {} res.headers.forEach((v, k) => (headers[k] = v)) ws.send(JSON.stringify({ id: msg.id, status: res.status, headers, body, })) } catch (err: any) { ws.send(JSON.stringify({ id: msg.id, status: 500, headers: { "content-type": "text/plain" }, body: "error: " + err.message, })) } } return promise }