diff --git a/server/lib/cf.ts b/server/lib/cf.ts index 80f8f7a..bb1de19 100644 --- a/server/lib/cf.ts +++ b/server/lib/cf.ts @@ -14,6 +14,25 @@ export function cfEnv(event: H3Event): Env { return env; } +/** + * The raw web `Request` for this event. On the Cloudflare module preset it + * lives in `event.context.cloudflare.request`; on other web runtimes h3 + * exposes it as `event.web.request`. + * + * NOTE: nitro pre-reads the request body into its internal buffers, so the + * returned Request's body stream is already consumed — use `readRawBody()` + * (or `readBody()`) to get the payload instead of `request.text()`. + */ +export function rawRequest(event: H3Event): Request { + const cf = (event.context.cloudflare as { request?: Request } | undefined)?.request; + const web = event.web?.request; + const req = cf ?? web; + if (!req) { + throw new Error("Raw request unavailable — run via wrangler dev/deploy"); + } + return req; +} + /** `waitUntil` from the CF execution context (no-op outside workers). */ export function cfWaitUntil(event: H3Event): (promise: Promise) => void { const cloudflare = event.context.cloudflare as diff --git a/server/routes/discord/interactions.post.ts b/server/routes/discord/interactions.post.ts index 100cf72..11a1401 100644 --- a/server/routes/discord/interactions.post.ts +++ b/server/routes/discord/interactions.post.ts @@ -1,6 +1,18 @@ +import { readRawBody } from "h3"; import { handleInteractionRequest } from "../../lib/drivers/discord/interactions"; -import { cfEnv } from "../../lib/cf"; +import { cfEnv, rawRequest } from "../../lib/cf"; -export default defineEventHandler((event) => - handleInteractionRequest(event.web!.request!, cfEnv(event)), -); +export default defineEventHandler(async (event) => { + // The raw Request is provided for headers/URL, but nitro consumes its body + // while buffering the payload — rebuild a fresh Request with the body from + // h3's internal buffers so the handler can read it (signature verification + // needs the exact raw bytes). + const source = rawRequest(event); + const body = await readRawBody(event, "utf8"); + const request = new Request(source.url, { + method: source.method, + headers: source.headers, + body, + }); + return handleInteractionRequest(request, cfEnv(event)); +}); diff --git a/server/routes/telegram/webhook.post.ts b/server/routes/telegram/webhook.post.ts index 75c8c35..8d0e93e 100644 --- a/server/routes/telegram/webhook.post.ts +++ b/server/routes/telegram/webhook.post.ts @@ -1,6 +1,14 @@ +import { readRawBody } from "h3"; import { handleTelegramWebhookRequest } from "../../lib/drivers/telegram/updates"; -import { cfEnv } from "../../lib/cf"; +import { cfEnv, rawRequest } from "../../lib/cf"; -export default defineEventHandler((event) => - handleTelegramWebhookRequest(event.web!.request!, cfEnv(event)), -); +export default defineEventHandler(async (event) => { + const source = rawRequest(event); + const body = await readRawBody(event, "utf8"); + const request = new Request(source.url, { + method: source.method, + headers: source.headers, + body, + }); + return handleTelegramWebhookRequest(request, cfEnv(event)); +});