diff --git a/src/app.ts b/src/app.ts index d48258f..45aed82 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,6 +5,7 @@ import { HTTPException } from "hono/http-exception"; import { engine, dynamodb } from "./instances.js"; import { touch } from "./services.js"; import { deleteResult, loadSortedResults } from "./stores.js"; +import { getAPIGatewayInput } from "./helpers.js"; const robotsTxt = ` User-agent: * @@ -111,6 +112,7 @@ app.get("/sentry/message", async (c) => { app.get("/sentry/error/handled", async (c) => { try { const e = new Error("handled error"); + e.name = "HandledError"; (e as any).foo = "bar"; throw e; } catch (e: unknown) { @@ -120,7 +122,10 @@ app.get("/sentry/error/handled", async (c) => { }); app.get("/sentry/error/unhandled", async (c) => { - throw new Error("unhandled error"); + const e = new Error("unhandled error"); + e.name = "UnhandledError"; + (e as any).a = 1; + throw e; }); app.get("/error/plain", async (c) => { @@ -134,6 +139,16 @@ app.get("/error/http", async (c) => { throw new HTTPException(403, { message: "hono-http-exception" }); }); +app.all("/dump", async (c) => { + const apiGateway = getAPIGatewayInput(); + if (!apiGateway) { + return c.json({ message: "apigateway not found" }); + } + + const { event, context } = apiGateway; + return c.json(event); +}); + app.get("*", async (c) => { throw new HTTPException(404, { message: `not found` }); }); diff --git a/src/handlers.ts b/src/handlers.ts index 0e996b8..a231b9c 100644 --- a/src/handlers.ts +++ b/src/handlers.ts @@ -1,9 +1,9 @@ import type { APIGatewayProxyHandlerV2, ScheduledHandler } from "aws-lambda"; import * as Sentry from "@sentry/serverless"; import { app } from "./app.js"; -import { handle } from "hono/aws-lambda"; import { touch } from "./services.js"; import { SENTRY_DSN, NODE_ENV } from "./settings.js"; +import { wrap_apigateway } from "./helpers.js"; if (SENTRY_DSN) { Sentry.AWSLambda.init({ @@ -18,9 +18,9 @@ if (SENTRY_DSN) { }); } -const http_inner = handle(app); +const http_inner = wrap_apigateway(app); export const http: APIGatewayProxyHandlerV2 = async (event, context) => { - const response = await http_inner(event as any); + const response = await http_inner(event, context); return response; }; diff --git a/src/helpers.ts b/src/helpers.ts new file mode 100644 index 0000000..0d807ab --- /dev/null +++ b/src/helpers.ts @@ -0,0 +1,40 @@ +import { AsyncLocalStorage } from "node:async_hooks"; +import { + APIGatewayProxyEvent, + APIGatewayProxyEventV2, + APIGatewayProxyStructuredResultV2, + Context, +} from "aws-lambda"; +import { Hono } from "hono"; +import { handle } from "hono/aws-lambda"; + +export type APIGatewayInput = { + event: APIGatewayProxyEventV2 | APIGatewayProxyEvent; + context: Context; +}; + +export const asyncLocalStorage = new AsyncLocalStorage(); + +export function getAPIGatewayInput(): APIGatewayInput | undefined { + return asyncLocalStorage.getStore(); +} + +export const wrap_apigateway = (app: Hono) => { + const handle_core = handle(app); + + return async ( + event: APIGatewayProxyEventV2 | APIGatewayProxyEvent, + context: Context, + ): Promise => { + const apigw: APIGatewayInput = { + event, + context, + }; + + const response = await asyncLocalStorage.run(apigw, async () => { + return await handle_core(event as any); + }); + + return response; + }; +}; diff --git a/src/router.ts b/src/router.ts deleted file mode 100644 index 4f5d098..0000000 --- a/src/router.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* -import path from "node:path"; -import { readFile } from "node:fs/promises"; -import { setTimeout } from "node:timers/promises"; -import { Router } from "itty-router"; -import { APIGatewayProxyResultV2 } from "aws-lambda"; -import { loadSortedResults, redis } from "./stores.js"; -import { MyRequest } from "./app.js"; - -export const router = Router(); - -router.get("/recent", async (req0) => { - const req = req0 as MyRequest; - const results = await loadSortedResults(redis); - const entries = results.map((result) => { - const { label, health } = result; - - let data: unknown; - switch (health.tag) { - case "ok": - data = { - tag: health.tag, - at: new Date(health.dateStr), - value: health.value, - }; - break; - case "error": - data = { - tag: health.tag, - at: new Date(health.dateStr), - reason: { - name: health.reason.name, - }, - }; - break; - case "ignore": - data = undefined; - break; - } - return [label, data] as const; - }); - - const output = Object.fromEntries(entries); - return { - statusCode: 200, - body: JSON.stringify(output, null, 2), - }; -}); - -router.get("/exc", async (req0) => { - const req = req0 as MyRequest; - const { event, context } = req.apiGateway; - - async function throwExc(message: string) { - await setTimeout(10); - throw new Error(message); - } - - try { - await throwExc("sample"); - } catch (e: any) { - console.error(e); - } finally { - return { - statusCode: 200, - body: JSON.stringify(event.requestContext.http, null, 2), - }; - } -}); - -router.get("/", async (req0): Promise => { - const req = req0 as MyRequest; - const fp = path.join(process.cwd(), "public", "index.html"); - const html = await readFile(fp, "utf-8"); - - return { - statusCode: 200, - body: html, - headers: { - "content-type": "text/html", - }, - }; -}); - -router.all("/dump", async (req0) => { - const req1 = req0 as MyRequest; - const { apiGateway, ...req } = req1; - const { event, context } = apiGateway; - - return { - statusCode: 200, - body: JSON.stringify({ req, event }, null, 2), - }; -}); - -router.all("*", () => { - const response: APIGatewayProxyResultV2 = { - statusCode: 404, - body: "Not Found", - }; - return response; -}); - -*/