diff --git a/ui/src/routes/books/[slug]/+page.server.ts b/ui/src/routes/books/[slug]/+page.server.ts index 484108e..cd814f4 100644 --- a/ui/src/routes/books/[slug]/+page.server.ts +++ b/ui/src/routes/books/[slug]/+page.server.ts @@ -29,6 +29,7 @@ export const load: PageServerLoad = async ({ params, locals }) => { return { book, chapters, - lastChapter: progress?.chapter ?? null + lastChapter: progress?.chapter ?? null, + isAdmin: locals.user?.role === 'admin' }; }; diff --git a/ui/src/routes/books/[slug]/+page.svelte b/ui/src/routes/books/[slug]/+page.svelte index 26c36a1..0c73655 100644 --- a/ui/src/routes/books/[slug]/+page.svelte +++ b/ui/src/routes/books/[slug]/+page.svelte @@ -21,6 +21,30 @@ const visibleChapters = $derived( data.chapters.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE) ); + + // ── Admin: rescrape ─────────────────────────────────────────────────────── + let scraping = $state(false); + let scrapeResult = $state<'queued' | 'busy' | 'error' | ''>(''); + + async function rescrape() { + if (scraping || !data.book.source_url) return; + scraping = true; + scrapeResult = ''; + try { + const res = await fetch('/api/scrape', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ url: data.book.source_url }) + }); + if (res.ok) scrapeResult = 'queued'; + else if (res.status === 409) scrapeResult = 'busy'; + else scrapeResult = 'error'; + } catch { + scrapeResult = 'error'; + } finally { + scraping = false; + } + } @@ -86,27 +110,65 @@ ({data.chapters.length}) - {#if totalPages > 1} -
+
+ {#if data.isAdmin && data.book.source_url} - {page + 1} / {totalPages} - -
- {/if} + {/if} + + {#if totalPages > 1} +
+ + {page + 1} / {totalPages} + +
+ {/if} +
+ {#if scrapeResult} +
+ {scrapeResult === 'queued' ? 'Rescrape queued — running in background.' : + scrapeResult === 'busy' ? 'Scraper is busy with another job. Try again shortly.' : + 'Failed to queue rescrape. Check server logs.'} +
+ {/if} + {#if data.chapters.length === 0}

No chapters available yet.

{:else}