share command

This commit is contained in:
Chris Wanstrath 2025-09-23 21:03:10 -07:00
parent 5797ff9ebc
commit cfa8f2795e
2 changed files with 77 additions and 34 deletions

14
app/nose/bin/share.ts Normal file
View File

@ -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 <app>`
}
if (!apps().includes(app)) {
return { error: `${app} not found` }
}
return await connectSneaker(app)
}

View File

@ -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<string, string> = {}
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<string, Connection> = {}
// returns the sneaker subdomain if successful
export async function connectSneaker(app: string): Promise<string> {
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<string>(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<string, string> = {}
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
}