feat: add configurable UCE error pages
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
Runtime
|
||||
error_pages
|
||||
unit_info
|
||||
units_list
|
||||
unit_compile
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
:title
|
||||
Configurable Error Pages
|
||||
|
||||
:sig
|
||||
page_compiling=site/errors/compiling.uce
|
||||
page_compiler_error=site/errors/compiler-error.uce
|
||||
page_runtime_error=site/errors/runtime-error.uce
|
||||
|
||||
:params
|
||||
page_compiling : UCE page rendered (status 503) while the requested page is being built
|
||||
page_compiler_error : UCE page rendered (status 500) when the requested page fails to build
|
||||
page_runtime_error : UCE page rendered (status 500) after a recovered fault or uncaught exception
|
||||
|
||||
:see
|
||||
>runtime
|
||||
0_Request
|
||||
0_DTree
|
||||
set_status
|
||||
|
||||
:content
|
||||
Three optional keys in `/etc/uce/settings.cfg` route error conditions to developer-defined UCE pages. Each key names a `.uce` file, either absolute or relative to the server's working directory. When a key is unset, or the named file does not exist, or the error page itself fails, the runtime's built-in plain-text behavior stands.
|
||||
|
||||
All three pages receive the error context in `context.call["error"]`:
|
||||
|
||||
- `type` — `compiling`, `compiler_error`, or `runtime_error`
|
||||
- `source` — the requested `.uce` file
|
||||
- `compiler_error` adds: `compiler_output` (raw clang output), `generated_cpp`, `status`, `details`
|
||||
- `runtime_error` adds: `title`, `details`, `request_uri`, `signal`, `signal_name`, `trace`, `generated_cpp`
|
||||
|
||||
A sensible status code is set before the page renders (`503` for compiling, `500` for errors); the page may override it with `set_status()` and set its own headers.
|
||||
|
||||
## page_compiling
|
||||
|
||||
Without this key, a request that hits an uncompiled or changed page blocks until the synchronous build finishes. With it, the runtime responds immediately with your page, hands the build to the proactive compiler, and the page can poll until the build lands:
|
||||
|
||||
```cpp
|
||||
RENDER(Request& context)
|
||||
{
|
||||
context.header["Refresh"] = "2";
|
||||
<><!doctype html><html><head><meta http-equiv="refresh" content="2"></head>
|
||||
<body>Building… this page reloads automatically.</body></html></>
|
||||
}
|
||||
```
|
||||
|
||||
The compiling page is only used while `PROACTIVE_COMPILE_ENABLED` is on (the default) — otherwise nothing would finish the build, and the runtime falls back to the blocking compile.
|
||||
|
||||
## page_compiler_error
|
||||
|
||||
Triggered when the entry page fails to build (components that fail mid-page keep the inline behavior, since the page is already half-rendered). `context.call["error"]["compiler_output"]` carries the full raw compiler output — show it on development hosts, hide it in production.
|
||||
|
||||
## page_runtime_error
|
||||
|
||||
Triggered after the worker recovers from a fatal signal or an uncaught exception. The worker has just recovered from a fault, so keep this page simple: static markup and the error fields, no database connections or heavy components.
|
||||
|
||||
## Recursion safety
|
||||
|
||||
While an error page renders, error-page handling is disabled: a compiling, broken, or crashing error page falls back to the built-in behavior instead of looping.
|
||||
|
||||
## Ready-made examples
|
||||
|
||||
`site/errors/compiling.uce`, `site/errors/compiler-error.uce`, and `site/errors/runtime-error.uce` in this repository are working examples — point the config keys at them or copy them into your site.
|
||||
@@ -0,0 +1,36 @@
|
||||
// Example page_compiler_error handler. Enable in /etc/uce/settings.cfg:
|
||||
// page_compiler_error=site/errors/compiler-error.uce
|
||||
// Served with status 500 when the requested unit fails to build. The full
|
||||
// compiler output stays available in context.call["error"], so this page can
|
||||
// show as much or as little of it as the deployment wants.
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree error = context.call["error"];
|
||||
|
||||
<>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Build failed</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; background: #1c1017; color: #fde8e8; margin: 0; min-height: 100vh; }
|
||||
main { max-width: 60rem; margin: 0 auto; padding: 3rem 2rem; }
|
||||
h1 { color: #f87171; }
|
||||
code { color: #fca5a5; }
|
||||
pre { background: #0f0a0d; color: #e2e8f0; padding: 1rem; border-radius: 0.5rem; overflow-x: auto; font-size: 0.85rem; line-height: 1.5; }
|
||||
.meta { color: #9f8a93; font-size: 0.9rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>This page failed to build</h1>
|
||||
<p>Source: <code><?= error["source"].to_string() ?></code></p>
|
||||
<p class="meta">Generated C++: <code><?= error["generated_cpp"].to_string() ?></code></p>
|
||||
<pre><?= error["compiler_output"].to_string() ?></pre>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Example page_compiling handler. Enable in /etc/uce/settings.cfg:
|
||||
// page_compiling=site/errors/compiling.uce
|
||||
// Served with status 503 while the requested unit is being (re)built; the
|
||||
// page refreshes itself until the build finishes and the real page renders.
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
context.header["Refresh"] = "2";
|
||||
String source = context.call["error"]["source"].to_string();
|
||||
|
||||
<>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="refresh" content="2">
|
||||
<title>Building page…</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; background: #0f172a; color: #e2e8f0; margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center; }
|
||||
main { text-align: center; max-width: 40rem; padding: 2rem; }
|
||||
.pulse { width: 3rem; height: 3rem; margin: 0 auto 1.5rem; border-radius: 50%; background: #38bdf8; animation: pulse 1.2s ease-in-out infinite; }
|
||||
@keyframes pulse { 0%, 100% { transform: scale(0.8); opacity: 0.5; } 50% { transform: scale(1); opacity: 1; } }
|
||||
code { color: #94a3b8; font-size: 0.85rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="pulse"></div>
|
||||
<h1>Building this page…</h1>
|
||||
<p>The page is being compiled and will load automatically in a moment.</p>
|
||||
<p><code><?= source ?></code></p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
</>
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Example page_runtime_error handler. Enable in /etc/uce/settings.cfg:
|
||||
// page_runtime_error=site/errors/runtime-error.uce
|
||||
// Served with status 500 after a recovered fault or uncaught exception. Keep
|
||||
// runtime error pages simple: they run in a worker that may have just
|
||||
// recovered from a fatal signal, so avoid database work or heavy components.
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
DTree error = context.call["error"];
|
||||
String signal_name = error["signal_name"].to_string();
|
||||
|
||||
<>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Something went wrong</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; background: #1c1017; color: #fde8e8; margin: 0; min-height: 100vh; }
|
||||
main { max-width: 60rem; margin: 0 auto; padding: 3rem 2rem; }
|
||||
h1 { color: #f87171; }
|
||||
code { color: #fca5a5; }
|
||||
pre { background: #0f0a0d; color: #e2e8f0; padding: 1rem; border-radius: 0.5rem; overflow-x: auto; font-size: 0.85rem; line-height: 1.5; }
|
||||
.meta { color: #9f8a93; font-size: 0.9rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Something went wrong</h1>
|
||||
<p><?= error["title"].to_string() ?></p>
|
||||
<p class="meta">Request: <code><?= error["request_uri"].to_string() ?></code></p>
|
||||
<? if(error["details"].to_string() != "") { ?>
|
||||
<p class="meta">Details: <?= error["details"].to_string() ?></p>
|
||||
<? } ?>
|
||||
<? if(signal_name != "") { ?>
|
||||
<p class="meta">Signal: <?= signal_name ?></p>
|
||||
<? } ?>
|
||||
<? if(error["trace"].to_string() != "") { ?>
|
||||
<pre><?= error["trace"].to_string() ?></pre>
|
||||
<? } ?>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
</>
|
||||
}
|
||||
Reference in New Issue
Block a user