/** * POST /api/scrape * * Proxies scrape requests to the Go scraper backend. * Admin-only — returns 403 if the authenticated user is not an admin. * * Request body (JSON): * { "url": "https://novelfire.net/book/..." } — scrape a single book * {} — scrape the full catalogue * * Responses mirror the Go scraper: * 202 Accepted — job enqueued * 409 Conflict — a scrape job is already running * 400 Bad Request * 403 Forbidden — not an admin */ import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { env } from '$env/dynamic/private'; import { log } from '$lib/server/logger'; const SCRAPER_URL = env.SCRAPER_API_URL ?? 'http://localhost:8080'; export const POST: RequestHandler = async ({ request, locals }) => { // Admin guard if (!locals.user || locals.user.role !== 'admin') { throw error(403, 'Forbidden'); } let body: { url?: string } = {}; try { body = await request.json(); } catch { // empty body is fine — means "scrape all" } // Decide which scraper endpoint to call const isBookScrape = typeof body.url === 'string' && body.url.length > 0; const endpoint = isBookScrape ? '/scrape/book' : '/scrape'; const upstream = `${SCRAPER_URL}${endpoint}`; let res: Response; try { res = await fetch(upstream, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: isBookScrape ? JSON.stringify({ url: body.url }) : undefined }); } catch (e) { log.error('scrape', 'scraper proxy network error', { endpoint, err: String(e) }); throw error(502, 'Could not reach scraper'); } if (!res.ok && res.status >= 500) { const text = await res.text().catch(() => ''); log.error('scrape', 'scraper returned error', { endpoint, status: res.status, body: text }); } const data = await res.json().catch(() => ({})); // Pass through the status code from the Go scraper (202, 409, 400, …) return json(data, { status: res.status }); };