Compare commits
No commits in common. "38d3481f8d0da384cac9917b437c13eaa3168e43" and "5797ff9ebce139abe9b21e59434200fbe7ef7023" have entirely different histories.
38d3481f8d
...
5797ff9ebc
|
|
@ -9,15 +9,16 @@
|
||||||
## Local Dev
|
## Local Dev
|
||||||
|
|
||||||
bun install
|
bun install
|
||||||
|
mkdir -p ~/nose/{bin,www}
|
||||||
bun dev
|
bun dev
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
Commands can return one of three types:
|
Commands can return one of three types:
|
||||||
|
|
||||||
- `string`
|
- string
|
||||||
- `{ error: string }`
|
- { error: string }
|
||||||
- `{ html: string }`
|
- { html: string }
|
||||||
|
|
||||||
They can also `throw` to display an error.
|
They can also `throw` to display an error.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
import { apps } from "app/src/webapp"
|
import { apps } from "app/src/webapp"
|
||||||
|
|
||||||
const devMode = process.env.NODE_ENV !== "production"
|
const devMode = process.env.BUN_HOT
|
||||||
|
|
||||||
export default async function () {
|
export default async function () {
|
||||||
const { stdout: hostname } = await $`hostname`.quiet()
|
const { stdout: hostname } = await $`hostname`.quiet()
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import { apps } from "app/src/webapp"
|
|
||||||
import { connectSneaker, sneakers, sneakerUrl } from "app/src/sneaker"
|
|
||||||
|
|
||||||
export default async function (app: string, subdomain = "") {
|
|
||||||
if (!app) {
|
|
||||||
let out = `usage: share <app> [subdomain]`
|
|
||||||
const apps = sneakers()
|
|
||||||
if (apps.length) {
|
|
||||||
out += "\n\nsharing\n" + apps.map(app => {
|
|
||||||
const url = sneakerUrl(app)
|
|
||||||
return `${app}: <a href="${url}">${url}</a>`
|
|
||||||
}).join("\n")
|
|
||||||
}
|
|
||||||
return { html: out }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!apps().includes(app)) {
|
|
||||||
return { error: `${app} not found` }
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = sneakerUrl(await connectSneaker(app, subdomain))
|
|
||||||
return { html: `<a href="${url}">${url}</a>` }
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import { apps } from "app/src/webapp"
|
|
||||||
import { disconnectSneaker, sneakers } from "app/src/sneaker"
|
|
||||||
|
|
||||||
export default async function (app: string) {
|
|
||||||
if (!app) {
|
|
||||||
return "usage: unshare <app>"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!apps().includes(app)) {
|
|
||||||
return { error: `${app} not found` }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sneakers().includes(app)) {
|
|
||||||
return { error: `${app} not shared` }
|
|
||||||
}
|
|
||||||
|
|
||||||
return (await disconnectSneaker(app)) ? "unshared" : `${app} wasn't shared`
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "bun src/server.tsx",
|
"start": "bun src/server.tsx",
|
||||||
"prod": "env NODE_ENV=production bun src/server.tsx",
|
|
||||||
"dev": "env BUN_HOT=1 bun --hot src/server.tsx",
|
"dev": "env BUN_HOT=1 bun --hot src/server.tsx",
|
||||||
"deploy": "./scripts/deploy.sh",
|
"deploy": "./scripts/deploy.sh",
|
||||||
"push": "./scripts/deploy.sh",
|
"push": "./scripts/deploy.sh",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# It isn't enough to modify this yet.
|
|
||||||
# You also need to manually update the nose-pluto.service file.
|
|
||||||
HOST="${HOST:-chris@nose-pluto.local}"
|
HOST="${HOST:-chris@nose-pluto.local}"
|
||||||
DEST="${DEST:-~/pluto}"
|
DEST="${DEST:-~/pluto}"
|
||||||
|
|
@ -1,110 +1,38 @@
|
||||||
import nose from "./server"
|
import app from "./server"
|
||||||
|
|
||||||
const SNEAKER_URL = "nose.space"
|
const SNEAKER_URL = "localhost:3100"
|
||||||
const SNEAKER_TLS = true
|
|
||||||
// const SNEAKER_URL = "localhost:3100"
|
|
||||||
// const SNEAKER_TLS = false
|
|
||||||
|
|
||||||
type Connection = {
|
const ws = new WebSocket(`ws://${SNEAKER_URL}/tunnel`)
|
||||||
subdomain: string
|
|
||||||
ws: any
|
|
||||||
close: boolean // manual close
|
|
||||||
}
|
|
||||||
const conns: Record<string, Connection> = {}
|
|
||||||
|
|
||||||
export function sneakerUrl(appOrSubdomain: string): string {
|
ws.onerror = e => console.log("sneaker error", e)
|
||||||
let conn = conns[appOrSubdomain]
|
|
||||||
if (!conn) {
|
ws.onmessage = async event => {
|
||||||
for (const appName of Object.keys(conns)) {
|
const msg = JSON.parse(event.data.toString())
|
||||||
if (conns[appName]?.subdomain === appOrSubdomain) {
|
try {
|
||||||
conn = conns[appName]
|
const req = new Request("http://localhost" + msg.path, {
|
||||||
break
|
method: msg.method,
|
||||||
}
|
headers: msg.headers,
|
||||||
}
|
body: msg.body || undefined,
|
||||||
if (!conn)
|
})
|
||||||
return "none"
|
|
||||||
|
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,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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<string> {
|
|
||||||
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<string>(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<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
|
|
||||||
}
|
|
||||||
|
|
@ -111,7 +111,7 @@ export async function publishDNS() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function publishAppDNS(app: string) {
|
function publishAppDNS(app: string) {
|
||||||
if (process.env.NODE_ENV !== "production") return
|
if (process.env.BUN_HOT) return
|
||||||
|
|
||||||
if (!dnsEntries[app])
|
if (!dnsEntries[app])
|
||||||
dnsEntries[app] = Bun.spawn(["avahi-publish", "-a", `${app}.${host}.local`, "-R", ip])
|
dnsEntries[app] = Bun.spawn(["avahi-publish", "-a", `${app}.${host}.local`, "-R", ip])
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user