diff --git a/src/commands.ts b/src/commands.ts index db04b02..82f7f5d 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -56,7 +56,7 @@ export async function loadCommandModule(cmd: string) { let sysCmdWatcher let usrCmdWatcher function startWatchers() { - expectDir(NOSE_BIN) + if (!expectDir(NOSE_BIN)) return sysCmdWatcher = watch(NOSE_SYS_BIN, async (event, filename) => sendAll({ type: "commands", data: await commands() }) diff --git a/src/dns.ts b/src/dns.ts index d188d51..12d337f 100644 --- a/src/dns.ts +++ b/src/dns.ts @@ -44,7 +44,7 @@ export function publishAppDNS(app: string) { let wwwWatcher function startWatcher() { - expectDir(NOSE_WWW) + if (!expectDir(NOSE_WWW)) return wwwWatcher = watch(NOSE_WWW, (event, filename) => { const www = apps() diff --git a/src/fatal.ts b/src/fatal.ts new file mode 100644 index 0000000..d54f7ea --- /dev/null +++ b/src/fatal.ts @@ -0,0 +1,10 @@ +//// +// We want to show a blue screen of death if NOSE has a fatal error. + +export let fatal: string | undefined + +export function setFatal(error: string) { + console.error(error) + if (!fatal) + fatal = error +} \ No newline at end of file diff --git a/src/html/error.tsx b/src/html/error.tsx new file mode 100644 index 0000000..3ee3689 --- /dev/null +++ b/src/html/error.tsx @@ -0,0 +1,60 @@ +import type { FC } from "hono/jsx" +import { css, js } from "../helpers" + +export const Error: FC = async ({ error }) => ( + <> + {css` + :root { + --letterbox: #CC8800; + --text: #FFBF40; + --bg: #201600; + } + * { color: var(--text); text-align: center; } + html { background: var(--letterbox); } + #content { background-color: var(--bg); } + + h1 { max-width: 38%; margin: 0 auto; margin-top: 100px; } + h2 { max-width: 80%; margin: 0 auto; margin-top: 10px; } + p { max-width: 90%; margin: 0 auto; margin-top: 150px; } + + .restart { max-width: 35%; } + .restart button { + font-size: 30px; + background: var(--letterbox); + color: var(--red); + } + + h1 { animation: glow 3s ease-in-out infinite alternate; } + + @keyframes glow { + 0% { + text-shadow: none; + } + 100% { + text-shadow: + 0 0 2px #FFAA33, + 0 0 4px #FF8800, + 0 0 6px #FF6600; + } + } + + `} + + {js` + window.addEventListener("click", async e => { + if (!e.target || !e.target.matches("button")) return + e.target.textContent = "[RESTARTING...]" + await fetch("/cmd/restart") + setTimeout(() => window.location.reload(), 3000) + }) + `} + +
{error}
++ +
+ > +) \ No newline at end of file diff --git a/src/nosedir.ts b/src/nosedir.ts index ca61e12..ad6b663 100644 --- a/src/nosedir.ts +++ b/src/nosedir.ts @@ -3,12 +3,13 @@ import { $ } from "bun" import { NOSE_DIR } from "./config" -import { isDir } from "./utils" +import { expectDir } from "./utils" -// Make NOSE_DIR if it doesn't exist +// Make NOSE_DIR if it doesn't exist export async function initNoseDir() { - if (isDir(NOSE_DIR)) return + if (expectDir(NOSE_DIR)) return await $`cp -r ./nose ${NOSE_DIR}` + expectDir(NOSE_DIR) } diff --git a/src/server.tsx b/src/server.tsx index 4af6762..6ab10bf 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -8,17 +8,20 @@ import color from "kleur" import type { Message } from "./shared/types" import { NOSE_ICON, NOSE_BIN, NOSE_WWW, NOSE_DATA, NOSE_DIR } from "./config" -import { transpile, isFile, tilde } from "./utils" +import { transpile, isFile, tilde, isDir } from "./utils" import { serveApp } from "./webapp" -import { initDNS } from "./dns" import { commands, commandPath, loadCommandModule } from "./commands" import { runCommandFn } from "./shell" import { send, addWebsocket, removeWebsocket, closeWebsockets } from "./websocket" +import { initSneakers, disconnectSneakers } from "./sneaker" +import { dispatchMessage } from "./dispatch" +import { fatal } from "./fatal" import { Layout } from "./html/layout" import { Terminal } from "./html/terminal" -import { dispatchMessage } from "./dispatch" -import { initSneakers, disconnectSneakers } from "./sneaker" +import { Error } from "./html/error" + +import { initDNS } from "./dns" import { initNoseDir } from "./nosedir" import { initCommands } from "./commands" @@ -43,6 +46,16 @@ app.use("*", async (c, next) => { console.log(fn(`${c.res.status} ${c.req.method} ${c.req.url} (${end - start}ms)`)) }) +app.use("*", async (c, next) => { + const error = fatal ? fatal : !isDir(NOSE_DIR) ? `NOSE_DIR doesn't exist: ${NOSE_DIR}` : undefined + + if (!error || (["/cmd/restart", "/cmd/reboot"].includes(c.req.path))) { + await next() + } else { + return c.html(