/** * POST /api/scrape/range * * Proxies range-scrape requests to the Go scraper backend at POST /scrape/book/range. * Admin-only. * * Request body (JSON): * { "url": "https://novelfire.net/book/...", "from": 50, "to": 100 } * "to" is optional — omit to scrape from "from" to the end. * * 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; from?: number; to?: number } = {}; try { body = await request.json(); } catch { throw error(400, 'Invalid JSON body'); } if (!body.url || typeof body.from !== 'number') { throw error(400, 'url and from are required'); } const upstream = `${SCRAPER_URL}/scrape/book/range`; let res: Response; try { res = await fetch(upstream, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: body.url, from: body.from, to: body.to }) }); } catch (e) { log.error('scrape/range', 'scraper proxy network error', { 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/range', 'scraper returned error', { status: res.status, body: text }); } const data = await res.json().catch(() => ({})); return json(data, { status: res.status }); };