fix(pwa): SW-Manifest trackt nur wirklich gecachte Rezepte
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:
hsiegeln
2026-04-18 16:50:26 +02:00
parent 51a88a4c58
commit 447ff2be32

View File

@@ -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;
}
}