projects/spikersoft/ngsw-config.json dataGroup api-book-metadata caches https://api.spikersoft.com/api/book/* with strategy: "freshness", maxAge: "7d", timeout: "3s". But the payload carries short-lived presigned MinIO URLs (coverUrl, coverVariants[].url) that expire ~1–2h after minting (2h TTL after spikersoft-backend#499).
Any freshness fallback — network slower than the 3s timeout, or offline — serves a book list where every cover URL is already dead, so every cover renders broken. Art Studio has no equivalent dataGroup and thus no equivalent exposure.
Secondary caveat (same dataGroup)
ngsw dataGroups key on URL and ignore the Authorization header, so per-user /api/book/my-books responses are cached under a URL that isn't user-scoped — a logout/login as a different user on the same browser can be served the previous user's cached book list until the network responds.
Fix
Drop maxAge for the cover-bearing book-list routes to ≤ the presigned TTL window (e.g. 30m), so any JSON the SW is willing to serve still contains live URLs. The epub-resource/page dataGroups (api-epub-resources, api-epub-pages) are more specific, match first, and are unaffected.
## Problem
`projects/spikersoft/ngsw-config.json` dataGroup `api-book-metadata` caches `https://api.spikersoft.com/api/book/*` with `strategy: "freshness"`, `maxAge: "7d"`, `timeout: "3s"`. But the payload carries short-lived presigned MinIO URLs (`coverUrl`, `coverVariants[].url`) that expire ~1–2h after minting (2h TTL after spikersoft-backend#499).
Any freshness *fallback* — network slower than the 3s timeout, or offline — serves a book list where **every cover URL is already dead**, so every cover renders broken. Art Studio has no equivalent dataGroup and thus no equivalent exposure.
## Secondary caveat (same dataGroup)
ngsw dataGroups key on URL and ignore the `Authorization` header, so per-user `/api/book/my-books` responses are cached under a URL that isn't user-scoped — a logout/login as a different user on the same browser can be served the previous user's cached book list until the network responds.
## Fix
Drop `maxAge` for the cover-bearing book-list routes to ≤ the presigned TTL window (e.g. `30m`), so any JSON the SW is willing to serve still contains live URLs. The epub-resource/page dataGroups (`api-epub-resources`, `api-epub-pages`) are more specific, match first, and are unaffected.
Related: spikersoft-backend#499 (stable memoized cover URLs), #618, #719.
Fix approach revised while implementing (spikersoft-angular PR #595): the ticket proposed dropping the dataGroup maxAge to ≤ the presigned TTL, but reading-journey.loadBooks() renders the shelf solely from getMyBooks() — there is no offline-manifest fallback — so a 30m maxAge would empty the shelf after half an hour offline, breaking the offline-reading scenario the SW cache exists to support.
Shipped instead:
shelf grid / hero / quick-view degrade to the placeholder on img error (dead presigned URL) rather than a broken image;
downloaded books render from an offline-persisted cover blob (#880), so the books that matter offline always have covers;
backend#499 makes URLs stable-per-hour + browser-cacheable, shrinking how often the SW even needs to fall back.
Still open from this ticket: the Authorization-blind cache key (per-user /api/book/my-books cached under a non-user-scoped URL). Options are clearing SW data caches on logout or dropping the dataGroup once an offline-manifest shelf fallback exists — leaving this ticket open for that decision.
Fix approach revised while implementing (spikersoft-angular PR #595): the ticket proposed dropping the dataGroup `maxAge` to ≤ the presigned TTL, but `reading-journey.loadBooks()` renders the shelf **solely** from `getMyBooks()` — there is no offline-manifest fallback — so a 30m `maxAge` would empty the shelf after half an hour offline, breaking the offline-reading scenario the SW cache exists to support.
Shipped instead:
- shelf grid / hero / quick-view degrade to the placeholder on `img` error (dead presigned URL) rather than a broken image;
- downloaded books render from an offline-persisted cover blob (#880), so the books that matter offline always have covers;
- backend#499 makes URLs stable-per-hour + browser-cacheable, shrinking how often the SW even needs to fall back.
Still open from this ticket: the Authorization-blind cache key (per-user `/api/book/my-books` cached under a non-user-scoped URL). Options are clearing SW data caches on logout or dropping the dataGroup once an offline-manifest shelf fallback exists — leaving this ticket open for that decision.
Audited against origin/master — one narrow item left. Staying open for it.
The ticket's literal ## Fix was deliberately not implemented, and that's correct.projects/spikersoft/ngsw-config.json:86-93 still carries "maxAge": "7d". Per comment 13369, dropping it to the presigned TTL would empty the shelf offline — the cure was worse than the disease. Not a gap; recording it so nobody "fixes" it later.
Shipped instead (angular PR #595, 5ca8e589): cover-error → placeholder degrade at my-books-shelf.component.html:35,101 and book-quick-view-dialog.component.html:21, plus the offline cover blobs from #880. Backend PR #499 supplies stable memoized presigned URLs so browsers can cache covers at all.
Genuinely still open: the Authorization-blind cache key. There is no logout-time purge of the SW data cache — git grep -n 'caches\.delete\|caches\.keys' origin/master hits only art-asset-mesh-blob-cache.ts and offline-cache.service.ts, neither tied to logout, and the api-book-metadata dataGroup is still registered. So after a logout/login on a shared device, one user's cached /api/book/* responses remain servable to the next. That's the remaining work.
Audited against `origin/master` — **one narrow item left. Staying open for it.**
**The ticket's literal `## Fix` was deliberately not implemented, and that's correct.** `projects/spikersoft/ngsw-config.json:86-93` still carries `"maxAge": "7d"`. Per comment 13369, dropping it to the presigned TTL would empty the shelf offline — the cure was worse than the disease. Not a gap; recording it so nobody "fixes" it later.
**Shipped instead** (angular PR #595, `5ca8e589`): cover-error → placeholder degrade at `my-books-shelf.component.html:35,101` and `book-quick-view-dialog.component.html:21`, plus the offline cover blobs from #880. Backend PR #499 supplies stable memoized presigned URLs so browsers can cache covers at all.
**Genuinely still open: the Authorization-blind cache key.** There is no logout-time purge of the SW data cache — `git grep -n 'caches\.delete\|caches\.keys' origin/master` hits only `art-asset-mesh-blob-cache.ts` and `offline-cache.service.ts`, neither tied to logout, and the `api-book-metadata` dataGroup is still registered. So after a logout/login on a shared device, one user's cached `/api/book/*` responses remain servable to the next. That's the remaining work.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
projects/spikersoft/ngsw-config.jsondataGroupapi-book-metadatacacheshttps://api.spikersoft.com/api/book/*withstrategy: "freshness",maxAge: "7d",timeout: "3s". But the payload carries short-lived presigned MinIO URLs (coverUrl,coverVariants[].url) that expire ~1–2h after minting (2h TTL after spikersoft-backend#499).Any freshness fallback — network slower than the 3s timeout, or offline — serves a book list where every cover URL is already dead, so every cover renders broken. Art Studio has no equivalent dataGroup and thus no equivalent exposure.
Secondary caveat (same dataGroup)
ngsw dataGroups key on URL and ignore the
Authorizationheader, so per-user/api/book/my-booksresponses are cached under a URL that isn't user-scoped — a logout/login as a different user on the same browser can be served the previous user's cached book list until the network responds.Fix
Drop
maxAgefor the cover-bearing book-list routes to ≤ the presigned TTL window (e.g.30m), so any JSON the SW is willing to serve still contains live URLs. The epub-resource/page dataGroups (api-epub-resources,api-epub-pages) are more specific, match first, and are unaffected.Related: spikersoft-backend#499 (stable memoized cover URLs), #618, #719.
Fix approach revised while implementing (spikersoft-angular PR #595): the ticket proposed dropping the dataGroup
maxAgeto ≤ the presigned TTL, butreading-journey.loadBooks()renders the shelf solely fromgetMyBooks()— there is no offline-manifest fallback — so a 30mmaxAgewould empty the shelf after half an hour offline, breaking the offline-reading scenario the SW cache exists to support.Shipped instead:
imgerror (dead presigned URL) rather than a broken image;Still open from this ticket: the Authorization-blind cache key (per-user
/api/book/my-bookscached under a non-user-scoped URL). Options are clearing SW data caches on logout or dropping the dataGroup once an offline-manifest shelf fallback exists — leaving this ticket open for that decision.Audited against
origin/master— one narrow item left. Staying open for it.The ticket's literal
## Fixwas deliberately not implemented, and that's correct.projects/spikersoft/ngsw-config.json:86-93still carries"maxAge": "7d". Per comment 13369, dropping it to the presigned TTL would empty the shelf offline — the cure was worse than the disease. Not a gap; recording it so nobody "fixes" it later.Shipped instead (angular PR #595,
5ca8e589): cover-error → placeholder degrade atmy-books-shelf.component.html:35,101andbook-quick-view-dialog.component.html:21, plus the offline cover blobs from #880. Backend PR #499 supplies stable memoized presigned URLs so browsers can cache covers at all.Genuinely still open: the Authorization-blind cache key. There is no logout-time purge of the SW data cache —
git grep -n 'caches\.delete\|caches\.keys' origin/masterhits onlyart-asset-mesh-blob-cache.tsandoffline-cache.service.ts, neither tied to logout, and theapi-book-metadatadataGroup is still registered. So after a logout/login on a shared device, one user's cached/api/book/*responses remain servable to the next. That's the remaining work.