import type { H3Event } from "h3"; import { createError, getRequestHeader } from "h3"; export async function readBounded( event: H3Event, max: number, ): Promise { const declared = getRequestHeader(event, "content-length"); if (declared && (!/^\d+$/u.test(declared) || Number(declared) > max)) throw createError({ statusCode: 413, statusMessage: "Request body exceeds the limit.", }); const chunks: Buffer[] = []; let total = 0; for await (const value of event.node.req) { const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value); total += chunk.byteLength; if (total > max) throw createError({ statusCode: 413, statusMessage: "Request body exceeds the limit.", }); chunks.push(chunk); } return Buffer.concat(chunks, total); } export async function readJson(event: H3Event, max: number): Promise { const body = await readBounded(event, max); try { return JSON.parse(body.toString("utf8")) as T; } catch { throw createError({ statusCode: 400, statusMessage: "Malformed JSON." }); } } export function requireContentType(event: H3Event, expected: string) { const type = ( (getRequestHeader(event, "content-type") || "").split(";", 1)[0] || "" ) .trim() .toLowerCase(); if (type !== expected) throw createError({ statusCode: 415, statusMessage: `Content-Type must be ${expected}.`, }); } export function bearerToken(event: H3Event): string | undefined { const value = getRequestHeader(event, "authorization") || ""; return /^Bearer ([A-Za-z0-9_-]{20,})$/u.exec(value)?.[1]; } export function publicHeaders(event: H3Event) { event.node.res.setHeader("cache-control", "no-store"); event.node.res.setHeader("referrer-policy", "no-referrer"); event.node.res.setHeader("x-content-type-options", "nosniff"); }