import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { getBook, listChapterIdx, getProgress, isBookSaved } from '$lib/server/pocketbase'; import { log } from '$lib/server/logger'; import { env } from '$env/dynamic/private'; const SCRAPER_URL = env.SCRAPER_API_URL ?? 'http://localhost:8080'; interface PreviewChapter { number: number; title: string; url: string; } /** * GET /api/book/[slug] * Returns book metadata, chapter list, progress, and library status. * Falls back to a live scraper preview if the book is not in PocketBase. * * Response shape mirrors BookDetailResponse in the iOS APIClient. */ export const GET: RequestHandler = async ({ params, locals }) => { const { slug } = params; // Try PocketBase first let book = await getBook(slug).catch((e) => { log.error('api/book', 'getBook failed', { slug, err: String(e) }); return null; }); if (book) { let chapters, progress, saved; try { [chapters, progress, saved] = await Promise.all([ listChapterIdx(slug), getProgress(locals.sessionId, slug, locals.user?.id), isBookSaved(locals.sessionId, slug, locals.user?.id) ]); } catch (e) { log.error('api/book', 'failed to load book detail data', { slug, err: String(e) }); error(500, 'Failed to load book'); } return json({ book, chapters, preview_chapters: null, in_lib: true, saved, last_chapter: progress?.chapter ?? null }); } // Fall back to live scraper preview try { const res = await fetch(`${SCRAPER_URL}/api/book-preview/${encodeURIComponent(slug)}`); if (!res.ok) { log.warn('api/book', 'book-preview returned error', { slug, status: res.status }); error(404, `Book "${slug}" not found`); } const preview: { in_lib: boolean; meta: { slug: string; title: string; author: string; cover: string; status: string; genres: string[]; summary: string; total_chapters: number; source_url: string; }; chapters: PreviewChapter[]; } = await res.json(); const previewBook = { id: '', slug: preview.meta.slug || slug, title: preview.meta.title, author: preview.meta.author, cover: preview.meta.cover, status: preview.meta.status, genres: preview.meta.genres ?? [], summary: preview.meta.summary, total_chapters: preview.meta.total_chapters, source_url: preview.meta.source_url, ranking: 0, meta_updated: '' }; return json({ book: previewBook, chapters: [], preview_chapters: preview.chapters, in_lib: preview.in_lib, saved: false, last_chapter: null }); } catch (e) { if (e instanceof Error && 'status' in e) throw e; log.error('api/book', 'book-preview fetch failed', { slug, err: String(e) }); error(404, `Book "${slug}" not found`); } };