make it work
This commit is contained in:
parent
3ec5b8d1e5
commit
9ce2995e0a
|
|
@ -1,7 +1,7 @@
|
||||||
// Show the webapps hosted on this NOSEputer.
|
// Show the webapps hosted on this NOSEputer.
|
||||||
|
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
import { apps } from "@/webapp/server"
|
import { apps } from "@/webapp/utils"
|
||||||
|
|
||||||
const devMode = process.env.NODE_ENV !== "production"
|
const devMode = process.env.NODE_ENV !== "production"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Share a webapp with the public internet.
|
// Share a webapp with the public internet.
|
||||||
|
|
||||||
import { apps } from "@/webapp/server"
|
import { apps } from "@/webapp/utils"
|
||||||
import { connectSneaker, sneakers, sneakerUrl } from "@/sneaker"
|
import { connectSneaker, sneakers, sneakerUrl } from "@/sneaker"
|
||||||
|
|
||||||
export default async function (app: string) {
|
export default async function (app: string) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Stop sharing a webapp with the public internet.
|
// Stop sharing a webapp with the public internet.
|
||||||
|
|
||||||
import { apps } from "@/webapp/server"
|
import { apps } from "@/webapp/utils"
|
||||||
import { disconnectSneaker, sneakers } from "@/sneaker"
|
import { disconnectSneaker, sneakers } from "@/sneaker"
|
||||||
|
|
||||||
export default async function (app: string) {
|
export default async function (app: string) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Publishes webapps as subdomains on your local network
|
// Publishes webapps as subdomains on your local network
|
||||||
|
|
||||||
import { watch } from "fs"
|
import { watch } from "fs"
|
||||||
import { apps } from "./webapp/server"
|
import { apps } from "./webapp/utils"
|
||||||
import { expectDir } from "./utils"
|
import { expectDir } from "./utils"
|
||||||
import { NOSE_DIR } from "./config"
|
import { NOSE_DIR } from "./config"
|
||||||
import { expectShellCmd } from "./utils"
|
import { expectShellCmd } from "./utils"
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,8 @@
|
||||||
// import { css } from "@nose"
|
// import { css } from "@nose"
|
||||||
|
|
||||||
import { Hono } from "hono"
|
import { Hono } from "hono"
|
||||||
import { type Handler, toResponse } from "./webapp/server"
|
import { type Handler } from "./webapp/server"
|
||||||
|
import { toResponse } from "./webapp/utils"
|
||||||
//
|
|
||||||
// command helpers
|
|
||||||
//
|
|
||||||
|
|
||||||
// (none for now)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// webapp helpers
|
// webapp helpers
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ import type { Message } from "./shared/types"
|
||||||
import { rewriteJsImports } from "./build"
|
import { rewriteJsImports } from "./build"
|
||||||
import { NOSE_ICON, NOSE_BIN, NOSE_DATA, NOSE_DIR, NOSE_ROOT_BIN, BUN_BIN, DEFAULT_PROJECT } from "./config"
|
import { NOSE_ICON, NOSE_BIN, NOSE_DATA, NOSE_DIR, NOSE_ROOT_BIN, BUN_BIN, DEFAULT_PROJECT } from "./config"
|
||||||
import { transpile, isFile, tilde, isDir } from "./utils"
|
import { transpile, isFile, tilde, isDir } from "./utils"
|
||||||
import { serveApp, apps, initWebapps } from "./webapp/server"
|
import { serveApp, initWebapps } from "./webapp/server"
|
||||||
|
import { apps } from "./webapp/utils"
|
||||||
import { commands, commandPath, loadCommandModule } from "./commands"
|
import { commands, commandPath, loadCommandModule } from "./commands"
|
||||||
import { runCommandFn } from "./shell"
|
import { runCommandFn } from "./shell"
|
||||||
import { send, addWebsocket, removeWebsocket, closeWebsockets } from "./websocket"
|
import { send, addWebsocket, removeWebsocket, closeWebsockets } from "./websocket"
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@
|
||||||
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 { join } from "path"
|
import { join } from "path"
|
||||||
import { readdirSync, watch } from "fs"
|
import { watch } from "fs"
|
||||||
import { sendAll } from "../websocket"
|
import { sendAll } from "../websocket"
|
||||||
import { expectDir } from "../utils"
|
import { expectDir } from "../utils"
|
||||||
import { NOSE_DIR, BUN_BIN } from "../config"
|
import { NOSE_DIR, BUN_BIN } from "../config"
|
||||||
import { isFile, isDir } from "../utils"
|
import { isFile } from "../utils"
|
||||||
|
import { apps, isApp, appDir, isStaticApp } 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
|
||||||
|
|
@ -20,12 +21,17 @@ export function initWebapps() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function serveApp(c: Context, subdomain: string): Promise<Response> {
|
export async function serveApp(c: Context, subdomain: string): Promise<Response> {
|
||||||
const port = await startApp(subdomain)
|
if (!isApp(subdomain)) return c.text(`App not found: ${subdomain}`, 404)
|
||||||
if (!port) return c.text(`App not found: ${subdomain}`, 404)
|
|
||||||
|
|
||||||
const staticPath = join(appDir(subdomain)!, "pub", c.req.path === "/" ? "/index.html" : c.req.path)
|
const staticPath = join(appDir(subdomain)!, "pub", c.req.path === "/" ? "/index.html" : c.req.path)
|
||||||
if (isFile(staticPath)) return serveStatic(staticPath)
|
if (isFile(staticPath)) return serveStatic(staticPath)
|
||||||
|
|
||||||
|
if (isStaticApp(subdomain)) return c.text("File not found", 404)
|
||||||
|
|
||||||
|
const port = await startApp(subdomain)
|
||||||
|
if (!port) return c.text(`App not found: ${subdomain}`, 404)
|
||||||
|
|
||||||
|
try {
|
||||||
const res = await fetch(`http://localhost:${port}${c.req.path}`, {
|
const res = await fetch(`http://localhost:${port}${c.req.path}`, {
|
||||||
method: c.req.method,
|
method: c.req.method,
|
||||||
headers: c.req.raw.headers,
|
headers: c.req.raw.headers,
|
||||||
|
|
@ -33,27 +39,9 @@ export async function serveApp(c: Context, subdomain: string): Promise<Response>
|
||||||
})
|
})
|
||||||
|
|
||||||
return new Response(res.body, { status: res.status, headers: res.headers })
|
return new Response(res.body, { status: res.status, headers: res.headers })
|
||||||
|
} catch {
|
||||||
|
return c.text("File not found", 404)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function apps(): string[] {
|
|
||||||
const apps: string[] = []
|
|
||||||
|
|
||||||
for (const entry of readdirSync(NOSE_DIR))
|
|
||||||
if (isApp(entry))
|
|
||||||
apps.push(entry)
|
|
||||||
|
|
||||||
return apps.sort()
|
|
||||||
}
|
|
||||||
|
|
||||||
function isApp(name: string): boolean {
|
|
||||||
return isFile(join(NOSE_DIR, name, "index.ts"))
|
|
||||||
|| isFile(join(NOSE_DIR, name, "index.tsx"))
|
|
||||||
|| isDir(join(NOSE_DIR, name, "pub"))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function appDir(name: string): string | undefined {
|
|
||||||
if (isApp(name))
|
|
||||||
return join(NOSE_DIR, name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startApp(name: string): Promise<string | undefined> {
|
async function startApp(name: string): Promise<string | undefined> {
|
||||||
|
|
@ -72,21 +60,11 @@ async function startApp(name: string): Promise<string | undefined> {
|
||||||
processes.set(name, { port, proc })
|
processes.set(name, { port, proc })
|
||||||
proc.exited.then(() => processes.delete(name))
|
proc.exited.then(() => processes.delete(name))
|
||||||
|
|
||||||
|
await Bun.sleep(50)
|
||||||
|
|
||||||
return port
|
return port
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function toResponse(source: string | Child | Response): Promise<Response> {
|
|
||||||
if (source instanceof Response)
|
|
||||||
return source
|
|
||||||
else if (typeof source === "string")
|
|
||||||
return new Response(source)
|
|
||||||
else
|
|
||||||
return new Response(await source?.toString(), {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "text/html; charset=utf-8"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function serveStatic(path: string): Response {
|
function serveStatic(path: string): Response {
|
||||||
const file = Bun.file(path)
|
const file = Bun.file(path)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,53 @@
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
|
import { readdirSync } from "fs"
|
||||||
|
import type { Child } from "hono/jsx"
|
||||||
import { NOSE_DIR } from "../config"
|
import { NOSE_DIR } from "../config"
|
||||||
|
import { isFile, isDir } from "../utils"
|
||||||
|
|
||||||
export async function appPath(appName: string): Promise<string | undefined> {
|
export async function appPath(appName: string): Promise<string | undefined> {
|
||||||
const ts = join(NOSE_DIR, appName, "index.ts")
|
const files = [join(NOSE_DIR, appName, "index.ts"), join(NOSE_DIR, appName, "index.tsx")]
|
||||||
const tsx = join(NOSE_DIR, appName, "index.tsx")
|
|
||||||
|
|
||||||
return [ts, tsx].find(async file => await Bun.file(file).exists())
|
for (const file of files)
|
||||||
|
if (await Bun.file(file).exists())
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function toResponse(source: string | Child | Response): Promise<Response> {
|
||||||
|
if (source instanceof Response)
|
||||||
|
return source
|
||||||
|
else if (typeof source === "string")
|
||||||
|
return new Response(source)
|
||||||
|
else
|
||||||
|
return new Response(await source?.toString(), {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/html; charset=utf-8"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function apps(): string[] {
|
||||||
|
const apps: string[] = []
|
||||||
|
|
||||||
|
for (const entry of readdirSync(NOSE_DIR))
|
||||||
|
if (isApp(entry))
|
||||||
|
apps.push(entry)
|
||||||
|
|
||||||
|
return apps.sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isApp(name: string): boolean {
|
||||||
|
return isFile(join(NOSE_DIR, name, "index.ts"))
|
||||||
|
|| isFile(join(NOSE_DIR, name, "index.tsx"))
|
||||||
|
|| isDir(join(NOSE_DIR, name, "pub"))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isStaticApp(name: string): boolean {
|
||||||
|
return !isFile(join(NOSE_DIR, name, "index.ts"))
|
||||||
|
&& !isFile(join(NOSE_DIR, name, "index.tsx"))
|
||||||
|
&& isDir(join(NOSE_DIR, name, "pub"))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appDir(name: string): string | undefined {
|
||||||
|
if (isApp(name))
|
||||||
|
return join(NOSE_DIR, name)
|
||||||
}
|
}
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
// This is the child process that runs a single webapp.
|
// This is the child process that runs a single webapp.
|
||||||
|
|
||||||
import { Hono } from "hono"
|
import { Hono } from "hono"
|
||||||
import { appPath } from "./utils"
|
import { appPath, toResponse } from "./utils"
|
||||||
|
|
||||||
const appName = Bun.argv[2]
|
const appName = Bun.argv[2]
|
||||||
if (!appName) {
|
if (!appName) {
|
||||||
console.log("usage: bun run webapp-worker <app-name>")
|
console.log("usage: bun run ./src/webapp/worker.ts <app-name>")
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,7 +19,7 @@ const handler = mod.default
|
||||||
if (typeof handler !== "function") throw `no default export in ${appName}`
|
if (typeof handler !== "function") throw `no default export in ${appName}`
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
app.all("*", handler)
|
app.all("*", async c => toResponse(await handler(c)))
|
||||||
const port = Number(process.env.PORT || 4000)
|
const port = Number(process.env.PORT || 4000)
|
||||||
Bun.serve({ port, fetch: app.fetch })
|
Bun.serve({ port, fetch: app.fetch })
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user