From 9ad4d762d7565a9a6cb6add1e9eec2d25d69035e Mon Sep 17 00:00:00 2001 From: dmitriylaukhin Date: Mon, 6 Jul 2026 12:39:01 +0500 Subject: [PATCH] Wrap /api/pdf handler in try/catch for JSON error contract Co-Authored-By: Claude Sonnet 5 --- src/app/api/pdf/route.ts | 43 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/app/api/pdf/route.ts b/src/app/api/pdf/route.ts index a18b806..4a2ba37 100644 --- a/src/app/api/pdf/route.ts +++ b/src/app/api/pdf/route.ts @@ -35,28 +35,28 @@ async function resolveUserId(req: NextRequest): Promise { } export async function GET(req: NextRequest) { - const userId = await resolveUserId(req); - if (!userId) return jsonError(401, "Нужен API-ключ или вход в аккаунт школы"); - - if (!(await hasPaidAccess(userId))) { - return jsonError(403, "Инструмент доступен студентам платных курсов школы"); - } - - const parsed = querySchema.safeParse(Object.fromEntries(req.nextUrl.searchParams)); - if (!parsed.success) return jsonError(422, "Проверьте параметры: url, format (A4|Letter), theme (light|dark)"); - - if ((await getBurstUsage(userId)) >= BURST_LIMIT) { - return jsonError(429, "Слишком часто: подождите минуту"); - } - const limit = getMonthlyLimit(); - const used = await getMonthlyUsage(userId); - if (used >= limit) { - return jsonError(429, `Лимит ${limit} PDF в месяц исчерпан`, { - "X-Uses-Count": String(used), "X-Max-Uses": String(limit), - }); - } - try { + const userId = await resolveUserId(req); + if (!userId) return jsonError(401, "Нужен API-ключ или вход в аккаунт школы"); + + if (!(await hasPaidAccess(userId))) { + return jsonError(403, "Инструмент доступен студентам платных курсов школы"); + } + + const parsed = querySchema.safeParse(Object.fromEntries(req.nextUrl.searchParams)); + if (!parsed.success) return jsonError(422, "Проверьте параметры: url, format (A4|Letter), theme (light|dark)"); + + if ((await getBurstUsage(userId)) >= BURST_LIMIT) { + return jsonError(429, "Слишком часто: подождите минуту", { "X-Max-Uses": String(getMonthlyLimit()) }); + } + const limit = getMonthlyLimit(); + const used = await getMonthlyUsage(userId); + if (used >= limit) { + return jsonError(429, `Лимит ${limit} PDF в месяц исчерпан`, { + "X-Uses-Count": String(used), "X-Max-Uses": String(limit), + }); + } + const { pdf, title } = await generateCleanPdf(parsed.data); await prisma.toolUsage.create({ data: { userId, tool: PDF_TOOL_ID } }); @@ -75,6 +75,7 @@ export async function GET(req: NextRequest) { return jsonError(422, e.message); } if (e instanceof RenderError) { + console.error("[clean-pdf] render", e); return jsonError(504, "Не получилось отрендерить страницу, попробуйте позже"); } console.error("[clean-pdf]", e);