feat: cover retry backfill + admin force-resync for manga & chapter (0.50.0)
Adds a per-tick cover-backfill pass to the crawler daemon so mangas whose cover download failed on first attempt get retried — the metadata pass's early-stop optimisation otherwise prevents the walk from revisiting them. Adds admin-only POST /admin/mangas/:id/resync and POST /admin/chapters/:id/resync that refetch metadata + cover (or chapter content with force_refetch) from the crawler source synchronously and return the refreshed row. Surfaced in the UI as "Force resync" buttons on the manga detail and reader pages, admin-only via session.user.is_admin. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
|
||||
import { request, type Page } from './client';
|
||||
import type { User } from './auth';
|
||||
import type { MangaDetail } from './mangas';
|
||||
import type { Chapter } from './chapters';
|
||||
|
||||
// ---- users -----------------------------------------------------------------
|
||||
|
||||
@@ -176,3 +178,39 @@ export type SystemStats = {
|
||||
export async function getSystemStats(): Promise<SystemStats> {
|
||||
return request<SystemStats>('/v1/admin/system');
|
||||
}
|
||||
|
||||
// ---- force resync ----------------------------------------------------------
|
||||
|
||||
export type MangaResyncResponse = {
|
||||
manga: MangaDetail;
|
||||
metadata_status: 'new' | 'updated' | 'unchanged';
|
||||
cover_fetched: boolean;
|
||||
};
|
||||
|
||||
export type ChapterResyncResponse = {
|
||||
chapter: Chapter;
|
||||
outcome: 'fetched' | 'skipped';
|
||||
/** Page count when `outcome === 'fetched'`; null when skipped. */
|
||||
pages: number | null;
|
||||
};
|
||||
|
||||
/** POST /v1/admin/mangas/:id/resync — refetches metadata + cover from
|
||||
* the manga's live crawler source. Long-running (one HTTP request per
|
||||
* Chromium nav + image download), so the UI should disable the trigger
|
||||
* and surface progress. */
|
||||
export async function resyncManga(id: string): Promise<MangaResyncResponse> {
|
||||
return request<MangaResyncResponse>(
|
||||
`/v1/admin/mangas/${encodeURIComponent(id)}/resync`,
|
||||
{ method: 'POST' }
|
||||
);
|
||||
}
|
||||
|
||||
/** POST /v1/admin/chapters/:id/resync — force-refetches a chapter's
|
||||
* pages even if `page_count > 0`. Same long-running caveat as
|
||||
* `resyncManga`. */
|
||||
export async function resyncChapter(id: string): Promise<ChapterResyncResponse> {
|
||||
return request<ChapterResyncResponse>(
|
||||
`/v1/admin/chapters/${encodeURIComponent(id)}/resync`,
|
||||
{ method: 'POST' }
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user