resilience
This commit is contained in:
parent
9c2550150b
commit
e3fa95ab68
|
|
@ -9,7 +9,7 @@ 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, initWebapps } from "./webapp/server"
|
import { serveApp, initWebapps, shutdownWebapps } from "./webapp/server"
|
||||||
import { apps } from "./webapp/utils"
|
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"
|
||||||
|
|
@ -217,6 +217,7 @@ if (process.env.BUN_HOT) {
|
||||||
globalThis.__hot_reload_cleanup = () => {
|
globalThis.__hot_reload_cleanup = () => {
|
||||||
closeWebsockets()
|
closeWebsockets()
|
||||||
disconnectSneakers()
|
disconnectSneakers()
|
||||||
|
shutdownWebapps()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const sig of ["SIGINT", "SIGTERM"] as const) {
|
for (const sig of ["SIGINT", "SIGTERM"] as const) {
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,14 @@ export type App = Hono | Handler
|
||||||
|
|
||||||
const processes = new Map<string, { port: string, proc: ReturnType<typeof Bun.spawn> }>()
|
const processes = new Map<string, { port: string, proc: ReturnType<typeof Bun.spawn> }>()
|
||||||
const restarting = new Set<string>()
|
const restarting = new Set<string>()
|
||||||
|
let nextPort = 4000
|
||||||
|
|
||||||
export async function initWebapps() {
|
export async function initWebapps() {
|
||||||
await startSubprocs()
|
await startSubprocs()
|
||||||
startWatcher()
|
startWatcher()
|
||||||
|
|
||||||
|
process.on("SIGINT", shutdownWebapps)
|
||||||
|
process.on("SIGTERM", shutdownWebapps)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function serveApp(c: Context, subdomain: string): Promise<Response> {
|
export async function serveApp(c: Context, subdomain: string): Promise<Response> {
|
||||||
|
|
@ -52,7 +56,7 @@ async function startApp(name: string): Promise<string | undefined> {
|
||||||
const existing = processes.get(name)
|
const existing = processes.get(name)
|
||||||
if (existing) return existing.port
|
if (existing) return existing.port
|
||||||
|
|
||||||
const port = String(4000 + processes.size)
|
const port = String(nextPort++)
|
||||||
const proc = Bun.spawn({
|
const proc = Bun.spawn({
|
||||||
cmd: [BUN_BIN, "run", "src/webapp/worker.ts", name],
|
cmd: [BUN_BIN, "run", "src/webapp/worker.ts", name],
|
||||||
env: { PORT: port },
|
env: { PORT: port },
|
||||||
|
|
@ -78,11 +82,16 @@ function serveStatic(path: string): Response {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function shutdown() {
|
export async function shutdownWebapps() {
|
||||||
|
wwwWatcher?.close()
|
||||||
|
nextPort = 4000
|
||||||
|
|
||||||
for (const [name, { port, proc }] of processes) {
|
for (const [name, { port, proc }] of processes) {
|
||||||
webappLog(name, "Shutting down")
|
webappLog(name, "Shutting down")
|
||||||
try { proc.kill() } catch { }
|
try { proc.kill() } catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processes.clear()
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,6 +99,8 @@ async function restartApp(name: string) {
|
||||||
if (restarting.has(name)) return
|
if (restarting.has(name)) return
|
||||||
restarting.add(name)
|
restarting.add(name)
|
||||||
|
|
||||||
|
if (isStaticApp(name)) return
|
||||||
|
|
||||||
webappLog(name, "restarting")
|
webappLog(name, "restarting")
|
||||||
const existing = processes.get(name)
|
const existing = processes.get(name)
|
||||||
if (existing) {
|
if (existing) {
|
||||||
|
|
@ -108,7 +119,7 @@ async function startSubprocs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let wwwWatcher
|
let wwwWatcher: any
|
||||||
function startWatcher() {
|
function startWatcher() {
|
||||||
if (!expectDir(NOSE_DIR)) return
|
if (!expectDir(NOSE_DIR)) return
|
||||||
|
|
||||||
|
|
@ -116,13 +127,10 @@ function startWatcher() {
|
||||||
if (!filename) return
|
if (!filename) return
|
||||||
|
|
||||||
const [appName,] = filename.split("/")
|
const [appName,] = filename.split("/")
|
||||||
if (appName && !filename.includes("/pub/"))
|
if (appName && /\.tsx?$/.test(filename))
|
||||||
restartApp(appName)
|
restartApp(appName)
|
||||||
|
|
||||||
if (/^.+\/index\.tsx?$/.test(filename))
|
if (/^.+\/index\.tsx?$/.test(filename))
|
||||||
sendAll({ type: "apps", data: apps() })
|
sendAll({ type: "apps", data: apps() })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on("SIGINT", shutdown)
|
|
||||||
process.on("SIGTERM", shutdown)
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,19 @@ if (!appName) {
|
||||||
const path = await appPath(appName)
|
const path = await appPath(appName)
|
||||||
if (!path) throw `Can't find app: ${appName}`
|
if (!path) throw `Can't find app: ${appName}`
|
||||||
|
|
||||||
const mod = await import(path)
|
let mod
|
||||||
|
try {
|
||||||
|
mod = await import(path)
|
||||||
|
} catch (err) {
|
||||||
|
webappLog(appName, `failed to import: ${err}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
const handler = mod.default
|
const handler = mod.default
|
||||||
if (typeof handler !== "function") throw `no default export in ${appName}`
|
if (typeof handler !== "function") {
|
||||||
|
webappLog(appName, `no default export`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
app.all("*", async c => toResponse(await handler(c)))
|
app.all("*", async c => toResponse(await handler(c)))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user