fix(pwa): SW-Manifest trackt nur wirklich gecachte Rezepte
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m18s
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m18s
Vorher: saveCachedIds(currentIds) hat alle Server-IDs gespeichert, auch wenn cacheRecipe still fehlschlug — beim nächsten sync-check wurden die fehlenden übersprungen, offline gab's 404. Jetzt: cacheRecipe + addToCache geben boolean zurück, nur die wirklich erfolgreich gecachten IDs landen im Manifest. Bei Update-Sync wird das Manifest aus (cached - toRemove + successful) berechnet. Zusätzlich console.warn in addToCache, damit Cache-Misses auf dem Pi debuggbar sind. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -117,10 +117,12 @@ async function runSync(isUpdate: boolean): Promise<void> {
|
||||
|
||||
await broadcast({ type: 'sync-start', total: worklist.length });
|
||||
|
||||
const successful = new Set<number>();
|
||||
let done = 0;
|
||||
const tasks = worklist.map((id) => async () => {
|
||||
const summary = summaries.find((s) => s.id === id);
|
||||
await cacheRecipe(id, summary?.image_path ?? null);
|
||||
const ok = await cacheRecipe(id, summary?.image_path ?? null);
|
||||
if (ok) successful.add(id);
|
||||
done += 1;
|
||||
await broadcast({ type: 'sync-progress', current: done, total: worklist.length });
|
||||
});
|
||||
@@ -130,7 +132,15 @@ async function runSync(isUpdate: boolean): Promise<void> {
|
||||
await removeRecipes(toRemove);
|
||||
}
|
||||
|
||||
await saveCachedIds(currentIds);
|
||||
// Manifest: für Update = (cached - toRemove) + neue successes
|
||||
// Für Initial = nur die diesmal erfolgreich gecachten
|
||||
const finalManifest = isUpdate
|
||||
? Array.from(
|
||||
new Set([...cachedIds.filter((id) => !toRemove.includes(id)), ...successful])
|
||||
)
|
||||
: Array.from(successful);
|
||||
|
||||
await saveCachedIds(finalManifest);
|
||||
await broadcast({ type: 'sync-done', lastSynced: Date.now() });
|
||||
} catch (e) {
|
||||
await broadcast({
|
||||
@@ -154,24 +164,33 @@ async function fetchAllSummaries(): Promise<RecipeSummary[]> {
|
||||
return result;
|
||||
}
|
||||
|
||||
async function cacheRecipe(id: number, imagePath: string | null): Promise<void> {
|
||||
async function cacheRecipe(id: number, imagePath: string | null): Promise<boolean> {
|
||||
const data = await caches.open(DATA_CACHE);
|
||||
const images = await caches.open(IMAGES_CACHE);
|
||||
await Promise.all([
|
||||
const [htmlOk, apiOk] = await Promise.all([
|
||||
addToCache(data, `/recipes/${id}`),
|
||||
addToCache(data, `/api/recipes/${id}`),
|
||||
imagePath && !/^https?:\/\//i.test(imagePath)
|
||||
? addToCache(images, `/images/${imagePath}`)
|
||||
: Promise.resolve()
|
||||
addToCache(data, `/api/recipes/${id}`)
|
||||
]);
|
||||
if (imagePath && !/^https?:\/\//i.test(imagePath)) {
|
||||
// Image-Fehler soll den Recipe-Eintrag nicht invalidieren (bei
|
||||
// manchen Rezepten gibt es schlicht kein Bild)
|
||||
await addToCache(images, `/images/${imagePath}`);
|
||||
}
|
||||
return htmlOk && apiOk;
|
||||
}
|
||||
|
||||
async function addToCache(cache: Cache, url: string): Promise<void> {
|
||||
async function addToCache(cache: Cache, url: string): Promise<boolean> {
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
if (res.ok) await cache.put(url, res);
|
||||
} catch {
|
||||
// Einzelne Fehler ignorieren — nächster Sync holt's nach.
|
||||
if (!res.ok) {
|
||||
console.warn(`[sw] cache miss ${url}: HTTP ${res.status}`);
|
||||
return false;
|
||||
}
|
||||
await cache.put(url, res);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.warn(`[sw] cache error ${url}:`, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user