comments, dns
This commit is contained in:
parent
acabf8c4c6
commit
7f695eb9eb
|
|
@ -1,3 +1,6 @@
|
||||||
|
////
|
||||||
|
// Manages the commands on disk, in NOSE_SYS_BIN and NOSE_BIN
|
||||||
|
|
||||||
import { Glob } from "bun"
|
import { Glob } from "bun"
|
||||||
import { watch } from "fs"
|
import { watch } from "fs"
|
||||||
import { sendAll } from "./websocket"
|
import { sendAll } from "./websocket"
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,33 @@
|
||||||
////
|
////
|
||||||
// Dispatch Messages
|
// Dispatch Messages received via WebSocket
|
||||||
|
|
||||||
import { basename } from "path"
|
import { basename } from "path"
|
||||||
import type { Message } from "./shared/types"
|
import type { Message } from "./shared/types"
|
||||||
import { runCommand } from "./shell"
|
import { runCommand } from "./shell"
|
||||||
import { send } from "./websocket"
|
import { send } from "./websocket"
|
||||||
import { isFile } from "./utils"
|
|
||||||
|
|
||||||
export async function dispatchMessage(ws: any, msg: Message) {
|
export async function dispatchMessage(ws: any, msg: Message) {
|
||||||
if (msg.type === "input") {
|
switch (msg.type) {
|
||||||
|
case "input":
|
||||||
|
await inputMessage(ws, msg); break
|
||||||
|
|
||||||
|
case "save-file":
|
||||||
|
await saveFileMessage(ws, msg); break
|
||||||
|
|
||||||
|
default:
|
||||||
|
send(ws, { type: "error", data: `unknown message: ${msg.type}` })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function inputMessage(ws: any, msg: Message) {
|
||||||
const result = await runCommand(msg.session || "", msg.id || "", msg.data as string)
|
const result = await runCommand(msg.session || "", msg.id || "", msg.data as string)
|
||||||
send(ws, { id: msg.id, type: "output", data: result })
|
send(ws, { id: msg.id, type: "output", data: result })
|
||||||
|
}
|
||||||
|
|
||||||
} else if (msg.type === "save-file") {
|
async function saveFileMessage(ws: any, msg: Message) {
|
||||||
if (msg.id && typeof msg.data === "string") {
|
if (msg.id && typeof msg.data === "string") {
|
||||||
await Bun.write(msg.id.replace("..", ""), msg.data, { createPath: true })
|
await Bun.write(msg.id.replace("..", ""), msg.data, { createPath: true })
|
||||||
send(ws, { type: "output", data: { status: "ok", output: `saved ${basename(msg.id)}` } })
|
send(ws, { type: "output", data: { status: "ok", output: `saved ${basename(msg.id)}` } })
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
send(ws, { type: "error", data: `unknown message: ${msg.type}` })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
55
app/src/dns.ts
Normal file
55
app/src/dns.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
////
|
||||||
|
// Publishes webapps as subdomains on your local network
|
||||||
|
|
||||||
|
import { watch } from "fs"
|
||||||
|
import { apps } from "./webapp"
|
||||||
|
import { expectDir } from "./utils"
|
||||||
|
import { NOSE_WWW } from "./config"
|
||||||
|
|
||||||
|
export const dnsEntries: Record<string, any> = {}
|
||||||
|
|
||||||
|
const { stdout: ipRaw } = await Bun.$`hostname -I | awk '{print $1}'`.quiet()
|
||||||
|
const { stdout: hostRaw } = await Bun.$`hostname`.quiet()
|
||||||
|
|
||||||
|
const ip = ipRaw.toString().trim()
|
||||||
|
const host = hostRaw.toString().trim()
|
||||||
|
let dnsInit = false
|
||||||
|
|
||||||
|
export async function initDNS() {
|
||||||
|
apps().forEach(publishAppDNS)
|
||||||
|
|
||||||
|
const signals = ["SIGINT", "SIGTERM"]
|
||||||
|
signals.forEach(sig =>
|
||||||
|
process.on(sig, () => {
|
||||||
|
for (const name in dnsEntries)
|
||||||
|
dnsEntries[name].kill("SIGTERM")
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
dnsInit = true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function publishAppDNS(app: string) {
|
||||||
|
if (!dnsInit) throw "publishAppDNS() must be called after initDNS()"
|
||||||
|
if (process.env.NODE_ENV !== "production") return
|
||||||
|
|
||||||
|
|
||||||
|
if (!dnsEntries[app])
|
||||||
|
dnsEntries[app] = Bun.spawn(["avahi-publish", "-a", `${app}.${host}.local`, "-R", ip])
|
||||||
|
|
||||||
|
return dnsEntries[app]
|
||||||
|
}
|
||||||
|
|
||||||
|
// exit process with error if no WWW dir
|
||||||
|
expectDir(NOSE_WWW)
|
||||||
|
|
||||||
|
const wwwWatcher = watch(NOSE_WWW, (event, filename) => {
|
||||||
|
const www = apps()
|
||||||
|
www.forEach(publishAppDNS)
|
||||||
|
for (const name in dnsEntries)
|
||||||
|
if (!www.includes(name)) {
|
||||||
|
dnsEntries[name].kill("SIGTERM")
|
||||||
|
delete dnsEntries[name]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
////
|
||||||
|
// Web server that serves shell commands, websocket connections, etc
|
||||||
|
|
||||||
import { Hono } from "hono"
|
import { Hono } from "hono"
|
||||||
import { serveStatic, upgradeWebSocket, websocket } from "hono/bun"
|
import { serveStatic, upgradeWebSocket, websocket } from "hono/bun"
|
||||||
import { prettyJSON } from "hono/pretty-json"
|
import { prettyJSON } from "hono/pretty-json"
|
||||||
|
|
@ -6,7 +9,8 @@ import color from "kleur"
|
||||||
import type { Message } from "./shared/types"
|
import type { Message } from "./shared/types"
|
||||||
import { NOSE_ICON, NOSE_BIN, NOSE_WWW } from "./config"
|
import { NOSE_ICON, NOSE_BIN, NOSE_WWW } from "./config"
|
||||||
import { transpile, isFile, tilde } from "./utils"
|
import { transpile, isFile, tilde } from "./utils"
|
||||||
import { apps, serveApp, publishDNS } from "./webapp"
|
import { apps, serveApp } from "./webapp"
|
||||||
|
import { initDNS } from "./dns"
|
||||||
import { commands } from "./commands"
|
import { commands } from "./commands"
|
||||||
import { send, addWebsocket, removeWebsocket, closeWebsockets } from "./websocket"
|
import { send, addWebsocket, removeWebsocket, closeWebsockets } from "./websocket"
|
||||||
|
|
||||||
|
|
@ -136,7 +140,7 @@ if (process.env.BUN_HOT) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
publishDNS()
|
initDNS()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
////
|
||||||
|
// Session storage. 1 browser tab = 1 session
|
||||||
|
|
||||||
import { AsyncLocalStorage } from "async_hooks"
|
import { AsyncLocalStorage } from "async_hooks"
|
||||||
|
|
||||||
export type Session = {
|
export type Session = {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
////
|
////
|
||||||
// runs commands and such.
|
// Runs commands and such on the server.
|
||||||
|
// This is the "shell" - the "terminal" is the browser UI.
|
||||||
|
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
import type { CommandResult, CommandOutput } from "./shared/types"
|
import type { CommandResult, CommandOutput } from "./shared/types"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
////
|
||||||
|
// Sneaker is our tunneling service that allows you to share your local NOSE webapps
|
||||||
|
// with the public internet. It requires a sneaker server, usually hosted by us.
|
||||||
|
|
||||||
import nose from "./server"
|
import nose from "./server"
|
||||||
|
|
||||||
const SNEAKER_URL = "nose.space"
|
const SNEAKER_URL = "nose.space"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
////
|
||||||
|
// Shell utilities and helper functions.
|
||||||
|
|
||||||
import { Hono } from "hono"
|
import { Hono } from "hono"
|
||||||
import { statSync } from "fs"
|
import { statSync } from "fs"
|
||||||
import { basename } from "path"
|
import { basename } from "path"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
|
////
|
||||||
|
// Hosting for your NOSE webapps!
|
||||||
|
|
||||||
import type { Child } from "hono/jsx"
|
import type { Child } from "hono/jsx"
|
||||||
import { type Context, Hono } from "hono"
|
import { type Context, Hono } from "hono"
|
||||||
import { renderToString } from "hono/jsx/dom/server"
|
import { renderToString } from "hono/jsx/dom/server"
|
||||||
import { join, dirname } from "path"
|
import { join, dirname } from "path"
|
||||||
import { readdirSync, watch } from "fs"
|
import { readdirSync } from "fs"
|
||||||
|
|
||||||
import { NOSE_WWW } from "./config"
|
import { NOSE_WWW } from "./config"
|
||||||
import { expectDir, isFile } from "./utils"
|
import { isFile } from "./utils"
|
||||||
|
|
||||||
export type Handler = (r: Context) => string | Child | Response | Promise<Response>
|
export type Handler = (r: Context) => string | Child | Response | Promise<Response>
|
||||||
export type App = Hono | Handler
|
export type App = Hono | Handler
|
||||||
|
|
@ -84,50 +87,3 @@ export function toResponse(source: string | Child | Response): Response {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// dns nonsense
|
|
||||||
//
|
|
||||||
|
|
||||||
const dnsEntries: Record<string, any> = {}
|
|
||||||
|
|
||||||
const { stdout: ipRaw } = await Bun.$`hostname -I | awk '{print $1}'`.quiet()
|
|
||||||
const { stdout: hostRaw } = await Bun.$`hostname`.quiet()
|
|
||||||
|
|
||||||
const ip = ipRaw.toString().trim()
|
|
||||||
const host = hostRaw.toString().trim()
|
|
||||||
|
|
||||||
export async function publishDNS() {
|
|
||||||
apps().forEach(publishAppDNS)
|
|
||||||
|
|
||||||
const signals = ["SIGINT", "SIGTERM"]
|
|
||||||
signals.forEach(sig =>
|
|
||||||
process.on(sig, () => {
|
|
||||||
for (const name in dnsEntries)
|
|
||||||
dnsEntries[name].kill("SIGTERM")
|
|
||||||
process.exit(0)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function publishAppDNS(app: string) {
|
|
||||||
if (process.env.NODE_ENV !== "production") return
|
|
||||||
|
|
||||||
if (!dnsEntries[app])
|
|
||||||
dnsEntries[app] = Bun.spawn(["avahi-publish", "-a", `${app}.${host}.local`, "-R", ip])
|
|
||||||
|
|
||||||
return dnsEntries[app]
|
|
||||||
}
|
|
||||||
|
|
||||||
// exit process with error if no WWW dir
|
|
||||||
expectDir(NOSE_WWW)
|
|
||||||
|
|
||||||
const wwwWatcher = watch(NOSE_WWW, (event, filename) => {
|
|
||||||
const www = apps()
|
|
||||||
www.forEach(publishAppDNS)
|
|
||||||
for (const name in dnsEntries)
|
|
||||||
if (!www.includes(name)) {
|
|
||||||
dnsEntries[name].kill("SIGTERM")
|
|
||||||
delete dnsEntries[name]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Loading…
Reference in New Issue
Block a user