import { NextRequest } from 'next/server'; import { saveCapture, getLatestCapture, clearCaptures } from '@/lib/db'; import { corsJson, corsPreflight } from '../cors'; /** Guard against a runaway page dumping tens of megabytes into SQLite every poll. */ const MAX_HTML_BYTES = 8 * 1024 * 1024; interface CapturePayload { url?: string; title?: string; html?: string; viewport?: unknown; elements?: unknown; capturedAt?: number; } /** Written by the Chromium extension each time it scrapes the target tab. */ export async function POST(req: NextRequest) { try { const body = await req.json() as CapturePayload; if (typeof body.html !== 'string' || typeof body.url !== 'string') { return corsJson({ error: '`url` and `html` are required' }, { status: 400 }); } if (body.html.length > MAX_HTML_BYTES) { return corsJson({ error: `HTML exceeds ${MAX_HTML_BYTES} bytes` }, { status: 413 }); } const capturedAt = typeof body.capturedAt === 'number' ? body.capturedAt : Date.now(); saveCapture({ url: body.url, title: typeof body.title === 'string' ? body.title : '', html: body.html, viewport: JSON.stringify(body.viewport ?? {}), elements: JSON.stringify(body.elements ?? []), captured_at: capturedAt, }); return corsJson({ ok: true, capturedAt, bytes: body.html.length }); } catch (err: any) { return corsJson({ error: err?.message ?? 'Failed to save capture' }, { status: 500 }); } } /** Read by the AutoBuyer page. Pass `?since=` to skip re-sending * an unchanged capture — the HTML blob is megabytes and the page polls often. */ export async function GET(req: NextRequest) { const row = getLatestCapture(); if (!row) return corsJson({ capture: null }); const since = Number(req.nextUrl.searchParams.get('since')); if (Number.isFinite(since) && since > 0 && since >= row.captured_at) { return corsJson({ unchanged: true }); } return corsJson({ capture: { url: row.url, title: row.title, html: row.html, viewport: safeParse(row.viewport, {}), elements: safeParse(row.elements, []), capturedAt: row.captured_at, }, }); } export async function DELETE() { clearCaptures(); return corsJson({ ok: true }); } export async function OPTIONS() { return corsPreflight(); } function safeParse(json: string, fallback: T): T { try { return JSON.parse(json) as T; } catch { return fallback; } }