attempt to detect unrecoverable notmuch state
All checks were successful
Generic zig build / build (push) Successful in 28s
All checks were successful
Generic zig build / build (push) Successful in 28s
This commit is contained in:
parent
a1de4e72c5
commit
691288e134
2 changed files with 49 additions and 1 deletions
13
src/main.zig
13
src/main.zig
|
|
@ -5,6 +5,12 @@ const auth = @import("auth.zig");
|
||||||
|
|
||||||
const version = @import("build_options").git_revision;
|
const version = @import("build_options").git_revision;
|
||||||
|
|
||||||
|
fn exitAfterDelay() void {
|
||||||
|
std.Thread.sleep(500 * std.time.ns_per_ms);
|
||||||
|
std.log.err("Notmuch search is in unrecoverable state: exiting", .{});
|
||||||
|
std.process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() !u8 {
|
pub fn main() !u8 {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
|
|
@ -208,6 +214,13 @@ fn queryHandler(db: *root.NotmuchDb, req: *httpz.Request, res: *httpz.Response)
|
||||||
const query = std.Uri.percentDecodeInPlace(query_buf);
|
const query = std.Uri.percentDecodeInPlace(query_buf);
|
||||||
|
|
||||||
var threads = db.search(query) catch |err| {
|
var threads = db.search(query) catch |err| {
|
||||||
|
if (err == error.CouldNotSearchThreads) {
|
||||||
|
res.status = 503;
|
||||||
|
try res.json(.{ .@"error" = "CouldNotSearchThreads", .fatal = true }, .{});
|
||||||
|
const exit_thread = std.Thread.spawn(.{}, exitAfterDelay, .{}) catch @panic("could not spawn thread to kill process");
|
||||||
|
exit_thread.detach();
|
||||||
|
return;
|
||||||
|
}
|
||||||
res.status = 500;
|
res.status = 500;
|
||||||
try res.json(.{ .@"error" = @errorName(err) }, .{});
|
try res.json(.{ .@"error" = @errorName(err) }, .{});
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,11 @@ button:disabled { background: #444; cursor: not-allowed; }
|
||||||
.login-box h2 { margin-bottom: 1rem; }
|
.login-box h2 { margin-bottom: 1rem; }
|
||||||
.login-box input { width: 100%; padding: 0.5rem; margin-bottom: 1rem; border: 1px solid #444; border-radius: 4px; background: #2a2a2a; color: #e0e0e0; }
|
.login-box input { width: 100%; padding: 0.5rem; margin-bottom: 1rem; border: 1px solid #444; border-radius: 4px; background: #2a2a2a; color: #e0e0e0; }
|
||||||
.login-box button { width: 100%; }
|
.login-box button { width: 100%; }
|
||||||
|
.restart-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 3000; align-items: center; justify-content: center; }
|
||||||
|
.restart-overlay.visible { display: flex; }
|
||||||
|
.restart-box { background: #252525; border: 1px solid #444; border-radius: 8px; padding: 2rem; text-align: center; }
|
||||||
|
.restart-box h2 { margin-bottom: 1rem; color: #ff6b6b; }
|
||||||
|
.restart-box p { margin-bottom: 1rem; color: #999; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -107,6 +112,14 @@ button:disabled { background: #444; cursor: not-allowed; }
|
||||||
<button onclick="submitLogin()">Login</button>
|
<button onclick="submitLogin()">Login</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="restart-overlay" class="restart-overlay">
|
||||||
|
<div class="restart-box">
|
||||||
|
<h2>Server Restarting</h2>
|
||||||
|
<p>The server encountered a fatal error and is restarting...</p>
|
||||||
|
<p id="retry-countdown">Retrying in <span id="retry-seconds">5</span> seconds</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
let currentQuery = 'tag:inbox';
|
let currentQuery = 'tag:inbox';
|
||||||
|
|
@ -130,10 +143,32 @@ async function api(endpoint) {
|
||||||
if (!retryRes.ok) throw new Error(`API error: ${retryRes.status}`);
|
if (!retryRes.ok) throw new Error(`API error: ${retryRes.status}`);
|
||||||
return retryRes.json();
|
return retryRes.json();
|
||||||
}
|
}
|
||||||
if (!res.ok) throw new Error(`API error: ${res.status}`);
|
if (!res.ok) {
|
||||||
|
if (res.status === 503) {
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.fatal) {
|
||||||
|
showRestartOverlay();
|
||||||
|
throw new Error('Fatal server error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(`API error: ${res.status}`);
|
||||||
|
}
|
||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showRestartOverlay() {
|
||||||
|
document.getElementById('restart-overlay').classList.add('visible');
|
||||||
|
let seconds = 5;
|
||||||
|
const countdown = setInterval(() => {
|
||||||
|
seconds--;
|
||||||
|
document.getElementById('retry-seconds').textContent = seconds;
|
||||||
|
if (seconds <= 0) {
|
||||||
|
clearInterval(countdown);
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
async function checkAuthStatus() {
|
async function checkAuthStatus() {
|
||||||
try {
|
try {
|
||||||
await api('auth/status');
|
await api('auth/status');
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue