mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 05:06:16 -08:00
docs(server-storage): conditional user-state writes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
340b1c3efa
commit
0183ddc61d
2 files changed
+16
-9
No files matched your search
@@ -88,7 +88,10 @@ Server storage adds versioned routes under `/v1`, which the PWA uses in
|
||||
replaced
|
||||
- `GET /v1/user-state`, `PUT|DELETE /v1/user-state/:namespace/:playlistId`:
|
||||
Xtream favorites, recently viewed, collection snapshots and playback
|
||||
positions, one JSON value per namespace and playlist
|
||||
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
|
||||
- `GET|PUT /v1/app-state/:key`
|
||||
|
||||
Server storage lives in `apps/web-backend/src/app/storage/`. With
|
||||
@@ -208,9 +211,12 @@ settings and guards read them in every build.
|
||||
(`iptvnator-server:cache-owner`); another one behind the same address
|
||||
starts clean. Also,
|
||||
re-reads run one at a time and keep entries edited while they were in
|
||||
flight or not yet saved. A re-read whose result does not fit the cache
|
||||
keeps the old cache and baseline together. A response body cut off after
|
||||
its headers counts as offline. Edits made before the first successful read
|
||||
flight or not yet saved. A response body cut off after its headers counts
|
||||
as offline. Each write is sent on the tag of the entry it was made
|
||||
against (`UserDataWriteQueue`); after a `412` the edit (before → after) is
|
||||
replayed on the server's current value, so another device's change to the
|
||||
same playlist's entry is kept. Deleting a playlist first drops its queued
|
||||
edits and waits for writes still travelling. Edits made before the first successful read
|
||||
are held as before → after and applied to the server's value once it
|
||||
arrives (`applyEdit`): a new device or a stale cache adds its favorite
|
||||
instead of replacing the server's list. They are saved in the tab too
|
||||
|
||||
@@ -132,8 +132,8 @@ All JSON. Errors: `{ message, status, code? }`.
|
||||
| `DELETE /v1/playlists/:id` | Delete the row and its user-state entries. |
|
||||
| `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 } } }`. |
|
||||
| `PUT/DELETE /v1/user-state/:namespace/:playlistId` | One entry. Namespaces are an allow-list. |
|
||||
| `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. |
|
||||
|
||||
ETags are `"<bootId>-<revision>"` from an in-memory per-playlist revision
|
||||
counter; the server is the only writer of its database.
|
||||
@@ -209,9 +209,10 @@ https://claude.ai/artifact/3WheJAgeNL5ag6kmtPrJcD
|
||||
|
||||
## 11. Risks
|
||||
|
||||
- **Concurrent edits on two devices**: last write wins per document or per
|
||||
Xtream map entry. Every playlist mutation re-reads first, so the window is
|
||||
one round trip.
|
||||
- **Concurrent edits on two devices**: last write wins per document.
|
||||
Every playlist mutation re-reads first, so the window is one round trip.
|
||||
Xtream map entries are written on the tag they were read at and replayed
|
||||
on the server's value after a `412`, so both devices' edits are kept.
|
||||
- **Large payloads**: M3U global favorites and backup export read all full
|
||||
documents, as IndexedDB does today.
|
||||
- **Open instances**: storing credentials raises the cost of an unprotected
|
||||
|
||||
Reference in new issue
Block a user