diff --git a/app/nose/bin/share.ts b/app/nose/bin/share.ts new file mode 100644 index 0000000..144d50b --- /dev/null +++ b/app/nose/bin/share.ts @@ -0,0 +1,14 @@ +import { apps } from "app/src/webapp" +import { connectSneaker } from "app/src/sneaker" + +export default async function (app: string) { + if (!app) { + return `usage: share ` + } + + if (!apps().includes(app)) { + return { error: `${app} not found` } + } + + return await connectSneaker(app) +} \ No newline at end of file diff --git a/app/src/sneaker.ts b/app/src/sneaker.ts index 9f6b1be..0907f20 100644 --- a/app/src/sneaker.ts +++ b/app/src/sneaker.ts @@ -1,38 +1,67 @@ -import app from "./server" +import nose from "./server" +// const SNEAKER_URL = "sneaker-ep2i.onrender.com" +// const SNEAKER_TLS = true const SNEAKER_URL = "localhost:3100" +const SNEAKER_TLS = false -const ws = new WebSocket(`ws://${SNEAKER_URL}/tunnel`) - -ws.onerror = e => console.log("sneaker error", e) - -ws.onmessage = async event => { - const msg = JSON.parse(event.data.toString()) - try { - const req = new Request("http://localhost" + msg.path, { - method: msg.method, - headers: msg.headers, - body: msg.body || undefined, - }) - - const res = await app.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, - })) - } +type Connection = { + subdomain: string + ws: any } +const connections: Record = {} + +// returns the sneaker subdomain if successful +export async function connectSneaker(app: string): Promise { + if (connections[app]) { + return connections[app].subdomain + } + + const ws = new WebSocket(`ws${SNEAKER_TLS ? "s" : ""}://${SNEAKER_URL}/tunnel?app=${app}`) + let resolve: (v: string) => void + let promise = new Promise(res => resolve = res) + + ws.onclose = (e) => delete connections[app] + + ws.onerror = e => console.error("sneaker error", e) + + ws.onmessage = async event => { + const msg = JSON.parse(event.data.toString()) + + if (msg.subdomain) { + connections[app] = { subdomain: "", ws } + 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 +} \ No newline at end of file