try to show error page on fatal errors
This commit is contained in:
parent
3ac1ba4f23
commit
14645980af
|
|
@ -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() })
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
10
src/fatal.ts
Normal file
10
src/fatal.ts
Normal file
|
|
@ -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
|
||||
}
|
||||
60
src/html/error.tsx
Normal file
60
src/html/error.tsx
Normal file
|
|
@ -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)
|
||||
})
|
||||
`}
|
||||
|
||||
<h1>Fatal Error</h1>
|
||||
<h2>NOSE failed to start properly.</h2>
|
||||
<br />
|
||||
<p>{error}</p>
|
||||
<p class="restart">
|
||||
<button>[RESTART]</button>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(<Layout><Error error={error} /></Layout>, 500)
|
||||
}
|
||||
})
|
||||
|
||||
app.on("GET", ["/js/:path{.+}", "/shared/:path{.+}"], async c => {
|
||||
let path = "./src/" + c.req.path.replace("..", ".")
|
||||
|
||||
|
|
@ -162,7 +175,7 @@ if (process.env.BUN_HOT) {
|
|||
}
|
||||
|
||||
//
|
||||
// production mode
|
||||
// production mode
|
||||
//
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Shell utilities and helper functions.
|
||||
|
||||
import { statSync } from "fs"
|
||||
import { basename } from "path"
|
||||
import { setFatal } from "./fatal"
|
||||
import { stat } from "fs/promises"
|
||||
|
||||
// Convert /Users/$USER or /home/$USER to ~ for simplicity
|
||||
|
|
@ -17,11 +17,13 @@ export function untilde(path: string): string {
|
|||
}
|
||||
|
||||
// End the process with an instructive error if a directory doesn't exist.
|
||||
export function expectDir(path: string) {
|
||||
export function expectDir(path: string): boolean {
|
||||
if (!isDir(path)) {
|
||||
console.error(`No ${basename(path)} directory detected.`)
|
||||
process.exit(1)
|
||||
setFatal(`Missing critical directory: ${path}`)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Is the given `path` a file?
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user