import "server-only"; import { AppError } from "@autoscholar/core"; import { NextResponse } from "next/server"; import type { ZodType } from "zod"; import { log } from "./db"; export function json(data: T, init?: ResponseInit): NextResponse { return NextResponse.json(data, { ...init, headers: { "cache-control": "no-store", ...(init?.headers ?? {}) }, }); } export function errorResponse(e: unknown): NextResponse { if (e instanceof AppError) return NextResponse.json(e.toJSON(), { status: e.status, headers: { "cache-control": "no-store" } }); if (e && typeof e === "object" && "issues" in e) { return NextResponse.json( { error: { code: "invalid", message: "validation failed", details: (e as { issues: unknown }).issues }, }, { status: 400 }, ); } const msg = (e as Error)?.message ?? "internal error"; log.error({ err: msg, stack: (e as Error)?.stack?.split("\n").slice(0, 4) }, "unhandled API error"); return NextResponse.json( { error: { code: "internal", message: process.env.NODE_ENV === "production" ? "internal error" : msg } }, { status: 500 }, ); } export async function parseBody(req: Request, schema: ZodType, maxBytes = 1024 * 1024): Promise { const len = Number(req.headers.get("content-length") ?? 0); if (len > maxBytes) throw new AppError("too_large", `body exceeds ${maxBytes} bytes`, 413); const text = await req.text(); if (text.length > maxBytes) throw new AppError("too_large", `body exceeds ${maxBytes} bytes`, 413); let raw: unknown; try { raw = text ? JSON.parse(text) : {}; } catch { throw new AppError("invalid_json", "body is not valid JSON", 400); } const r = schema.safeParse(raw); if (!r.success) throw new AppError("invalid", "validation failed", 400, r.error.issues); return r.data; } export function parseQuery(url: string, schema: ZodType): T { const u = new URL(url); const obj: Record = {}; u.searchParams.forEach((v, k) => { obj[k] = v; }); const r = schema.safeParse(obj); if (!r.success) throw new AppError("invalid", "invalid query", 400, r.error.issues); return r.data; } export const handler = (fn: (req: Request, ctx: { params: Promise> }) => Promise) => async (req: Request, ctx: { params: Promise> }) => { try { return await fn(req, ctx); } catch (e) { return errorResponse(e); } };