feat: correlate server errors and monitor application health

This commit is contained in:
syntaxbullet
2026-09-04 18:30:34 +02:00
parent 661bc6c346
commit 9c8a96ba03
10 changed files with 143 additions and 6 deletions

View File

@@ -1,3 +1,4 @@
import { errorDetails } from "./ops/monitor";
import { createAccountRoutes } from "./account/routes";
import { createHabitRoutes } from "./habits/routes";
import { ApiError } from "./habits/service";
@@ -9,11 +10,18 @@ import type { FetchDiscord } from "./auth/discord";
import { createSharingRoutes, type DiscordFetch } from "./sharing/routes";
import type { DiscordSharingConfig } from "./sharing/config";
export function createApi(db: AppDatabase, config: AuthConfig, request?: FetchDiscord, now?: () => number, discordRequest?: DiscordFetch, sharingConfig: DiscordSharingConfig = { token: "", channelId: "" }) {
export function createApi(db: AppDatabase, config: AuthConfig, request?: FetchDiscord, now?: () => number, discordRequest?: DiscordFetch, sharingConfig: DiscordSharingConfig = { token: "", channelId: "" }, operations: { healthy?: () => boolean; log?: (event: Record<string, unknown>) => void } = {}) {
const app = new Hono<AuthEnv>();
app.use("*", async (c, next) => {
const requestId = crypto.randomUUID();
c.set("requestId", requestId); c.header("X-Request-ID", requestId);
await next();
});
const auth = createAuth(db, config, request, now);
app.get("/api/health", c => {
c.header("Cache-Control", "no-store");
db.get(sql`SELECT 1`);
if (operations.healthy && !operations.healthy()) return c.json({ status: "degraded" }, 503);
return c.json({ status: "ok" });
});
app.route("/api/auth", auth.routes);
@@ -25,7 +33,11 @@ export function createApi(db: AppDatabase, config: AuthConfig, request?: FetchDi
app.onError((_error, c) => {
c.header("Cache-Control", "no-store");
if (_error instanceof ApiError) return c.json({ error: _error.message }, _error.status);
return c.json({ error: "Internal server error" }, 500);
const requestId = c.get("requestId");
const event = { event: "request_failed", timestamp: new Date().toISOString(), requestId, method: c.req.method,
route: c.req.routePath, ...errorDetails(_error) };
(operations.log ?? (event => console.error(JSON.stringify(event))))(event);
return c.json({ error: "Internal server error", requestId }, 500);
});
return app;
}