From 82813bba5a70474ceae023be91f100b07c97a1b1 Mon Sep 17 00:00:00 2001 From: 0X-SquidSol Date: Mon, 13 Apr 2026 11:50:21 -0400 Subject: [PATCH 1/2] test: align prices test with upstream sort order change Upstream changed /prices/:slab to ascending order + limit 1500 (for chart rendering), but the test still expected descending + 100. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/routes/prices.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/routes/prices.test.ts b/tests/routes/prices.test.ts index ac30a5e..b9278fc 100644 --- a/tests/routes/prices.test.ts +++ b/tests/routes/prices.test.ts @@ -122,8 +122,8 @@ describe("prices routes", () => { expect(res.status).toBe(200); const data = await res.json(); expect(data.prices).toHaveLength(2); - expect(mockSupabase.order).toHaveBeenCalledWith("timestamp", { ascending: false }); - expect(mockSupabase.limit).toHaveBeenCalledWith(100); + expect(mockSupabase.order).toHaveBeenCalledWith("timestamp", { ascending: true }); + expect(mockSupabase.limit).toHaveBeenCalledWith(1500); }); it("should return 400 for invalid slab", async () => { From 191adca03a95f19220594eb0ac3bdd88e813d41a Mon Sep 17 00:00:00 2001 From: 0X-SquidSol Date: Thu, 9 Apr 2026 13:15:07 -0400 Subject: [PATCH 2/2] fix: add request ID tracking for production debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No request correlation existed — if a user reported "I got a 500 at 3:15pm", ops could not find that specific request in logs or Sentry. Adds Hono's built-in requestId() middleware which: - Generates a UUID per request (or respects incoming X-Request-Id) - Sets X-Request-Id response header on all responses - Available via c.get("requestId") throughout the middleware chain Integration points: - Global error handler: logs requestId, includes it in error JSON response - Sentry middleware: tags every scope with request_id for event correlation - Error responses now include requestId so clients can quote it in reports Co-Authored-By: Claude Opus 4.6 (1M context) --- src/index.ts | 13 +++++++++++-- src/middleware/sentry.ts | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 597708e..263b16b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import { Hono } from "hono"; import { cors } from "hono/cors"; import { compress } from "hono/compress"; import { bodyLimit } from "hono/body-limit"; +import { requestId } from "hono/request-id"; import { serve } from "@hono/node-server"; import { createLogger, sendInfoAlert, getSupabase, sendCriticalAlert, truncateErrorMessage } from "@percolator/shared"; import { initSentry, sentryMiddleware, flushSentry } from "./middleware/sentry.js"; @@ -106,6 +107,10 @@ app.use("*", bodyLimit({ onError: (c) => c.json({ error: "Request body too large" }, 413), })); +// Request ID — generates a UUID per request for log correlation and debugging. +// Respects incoming X-Request-Id headers (e.g., from load balancers). +app.use("*", requestId()); + // Default-deny for mutation methods. Until write endpoints are added, // reject any POST/PUT/DELETE/PATCH requests that reach the API. // When write routes are needed, apply requireApiKey() from middleware/auth.ts @@ -211,13 +216,15 @@ app.get("/", (c) => c.json({ // Global error handler app.onError((err, c) => { + const reqId = c.get("requestId"); logger.error("Unhandled error", { + requestId: reqId, error: truncateErrorMessage(err.message, 120), stack: truncateErrorMessage(err.stack ?? "", 500), endpoint: c.req.path, method: c.req.method }); - + // Report to Sentry (sentryMiddleware may have already captured it, // but this ensures errors from middleware chain are also caught) try { @@ -226,14 +233,16 @@ app.onError((err, c) => { endpoint: c.req.path, method: c.req.method, handler: "onError", + request_id: reqId, }, }); } catch (_sentryErr) {} - + // Truncate error message for API response (details only in development) const showDetails = process.env.NODE_ENV !== "production"; return c.json({ error: "Internal server error", + requestId: reqId, ...(showDetails && { details: truncateErrorMessage(err.message, 200) }) }, 500); }); diff --git a/src/middleware/sentry.ts b/src/middleware/sentry.ts index 15f73bb..72e2b4e 100644 --- a/src/middleware/sentry.ts +++ b/src/middleware/sentry.ts @@ -54,6 +54,8 @@ export function sentryMiddleware(): MiddlewareHandler { // Set request context scope.setTag("http.method", c.req.method); scope.setTag("http.path", c.req.path); + const reqId = c.get("requestId"); + if (reqId) scope.setTag("request_id", reqId); // Set a hashed API key fingerprint as pseudonymous user ID. // Avoid sending any raw key material (even a prefix) to third-party services.