fix(server-storage): refuse user state for deleted playlists

PUT /v1/user-state/:namespace/:playlistId answers 404 playlist_missing
when the server has no such playlist, so an edit queued on another device
(including its conflict replay with If-None-Match: *) cannot recreate the
state of a playlist deleted meanwhile. The client drops such an edit and
its cache entry instead of retrying; page-hide writes report it as gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-12 23:43:43 +02:00
1 parent 716580cb16
commit f956758cce
8 files changed
+114 -8

No files matched your search

+3 -1
View File
@@ -92,7 +92,9 @@ Server storage adds versioned routes under `/v1`, which the PWA uses in
positions, one JSON value per namespace and playlist; `GET` adds a
content tag per entry (`tags`), and a write with `If-Match: "<tag>"` or
`If-None-Match: *` answers `412` with the current value and tag when the
entry changed since
entry changed since, and a `PUT` for a playlist the server does not have
answers `404 playlist_missing` (the client drops that edit), so an edit
queued on another device cannot bring a deleted playlist's state back
- `GET|PUT /v1/app-state/:key`
Server storage lives in `apps/web-backend/src/app/storage/`. With
@@ -133,7 +133,7 @@ All JSON. Errors: `{ message, status, code? }`.
| `DELETE /v1/playlists` | Delete all playlists and all user state. |
| `GET/PUT /v1/app-state/:key` | `{ value }`. |
| `GET /v1/user-state` | `{ entries: { [namespace]: { [playlistId]: value } }, tags }`, a content tag per entry. |
| `PUT/DELETE /v1/user-state/:namespace/:playlistId` | One entry. Namespaces are an allow-list. `If-Match: "<tag>"` or `If-None-Match: *` makes it conditional: `412` with the current value and tag. |
| `PUT/DELETE /v1/user-state/:namespace/:playlistId` | One entry. Namespaces are an allow-list. `If-Match: "<tag>"` or `If-None-Match: *` makes it conditional: `412` with the current value and tag. A `PUT` for a playlist the server does not have answers `404 playlist_missing`. |
ETags are `"<bootId>-<revision>"` from an in-memory per-playlist revision
counter; the server is the only writer of its database.