Server-controlled app updates, optional or forced
The gateway decides whether a TV may keep running its current build. Clients send X-Memby-Version on every request and ask GET /v1/update on each launch; the verdict is none, optional or mandatory. - internal/appupdate holds the decision as pure, tested logic: below minimumVersion is mandatory, below latestVersion is optional. - Admin page gains an App updates section — latest version, APK URL, notes, and a "Require this update" toggle that sets the forced floor. - Client shows a dismissable prompt for optional, and a full-screen panel that swallows Back for mandatory. Instructions say what the system installer will ask before it asks. Two safeguards: a client that cannot report its version is never forced, and the client ignores a verdict with no download URL, so a half-configured policy cannot produce an unblockable screen with a dead button. An unreachable gateway shows nothing. The verdict is deliberately not part of /v1/home: that payload is cached per user, while this answer depends on the requesting client's version. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
08360b75e4
commit
62f6345a40
@@ -48,6 +48,7 @@ headers attached.
|
||||
| GET | `/v1/auth/session` | Confirm a stored token is still valid |
|
||||
| GET | `/v1/home?limit=` | **Every launcher row in one response** |
|
||||
| GET | `/v1/recommendations?refresh=1` | Recommendation rows alone; `refresh` forces a rebuild |
|
||||
| GET | `/v1/update` | Whether this client (per `X-Memby-Version`) must update |
|
||||
| GET | `/v1/screensaver?limit=` | Backdrop pool, cached and shuffled per request |
|
||||
| GET | `/v1/search?q=&limit=` | Library search |
|
||||
| GET | `/v1/items/{id}` | Full metadata for one item |
|
||||
@@ -131,6 +132,7 @@ gateway is reachable from outside the LAN.
|
||||
| GET | `/admin/api/status` | Library counts, sync history, maintenance state |
|
||||
| POST | `/admin/api/sync` | `{"kind":"full"}` or `{"kind":"incremental"}` |
|
||||
| POST | `/admin/api/maintenance` | `{"enabled":true,"message":"…"}` |
|
||||
| POST | `/admin/api/update-policy` | `{"enabled":true,"latestVersion":"0.1.54","downloadUrl":"…","required":false}` |
|
||||
| GET | `/admin/api/analytics?days=7` | Row engagement |
|
||||
|
||||
## Library import
|
||||
@@ -172,6 +174,36 @@ The switch lives in Postgres, not memory, so a restart cannot quietly bring the
|
||||
up mid-repair. Each instance caches it and re-reads every 30 seconds, so toggling it
|
||||
directly in the database works too.
|
||||
|
||||
## App update policy
|
||||
|
||||
The gateway decides whether a TV may keep running its current build. Clients send
|
||||
`X-Memby-Version` on every request and ask `GET /v1/update` on each launch; the verdict is
|
||||
`none`, `optional` or `mandatory`.
|
||||
|
||||
Set it on the admin page: **latest version**, **APK URL** (normally the same file the
|
||||
landing page serves), release notes, and a **Require this update** toggle.
|
||||
|
||||
- *Optional* — a dismissable prompt. Dismissal lasts for that session only.
|
||||
- *Required* — a full-screen panel over the home screen with no way past it. Back is
|
||||
swallowed and there is one button. Use it when a build is genuinely unusable, not for
|
||||
ordinary releases.
|
||||
|
||||
"Required" sets `minimumVersion = latestVersion`; anything below that floor is forced.
|
||||
`minimumVersion` can also be set directly for a staged rollout where the forced floor is
|
||||
older than the newest build.
|
||||
|
||||
Two deliberate safeguards, both tested in `internal/appupdate`:
|
||||
|
||||
- A client that cannot report a version is **never** forced. It would otherwise be stuck
|
||||
behind a prompt it may have no way to satisfy.
|
||||
- The client only shows a verdict carrying a download URL, so a misconfigured policy
|
||||
cannot produce an unblockable screen with a dead button. An unreachable gateway shows
|
||||
nothing at all.
|
||||
|
||||
The verdict is deliberately *not* part of `/v1/home`: that payload is cached per user,
|
||||
while this answer depends on the requesting client's version, so sharing a cache entry
|
||||
would hand one TV's answer to another.
|
||||
|
||||
## Row analytics
|
||||
|
||||
The TV reports three signals per row — `impression` (drawn), `focus` (the remote landed
|
||||
|
||||
Reference in New Issue
Block a user