fix: add PUT /tasks/mirror (array full-replace of desktop Idle backlog)

The desktop pushes its full Idle backlog as a JSON array to /tasks/mirror, not per-task.
Previously /tasks/mirror matched tasks/[id].put.ts (id=mirror) and rejected the array with
400. New static route validates per-element, accepts empty arrays, upserts each as
consumed=true (desktop-owned), deletes consumed=true rows not in the array, and leaves
web-created consumed=false rows untouched. Mirrors PUT /lists.
This commit is contained in:
2026-06-10 09:35:33 +00:00
parent 812048a0b5
commit 65543cb6ee
4 changed files with 146 additions and 6 deletions

View File

@@ -37,13 +37,15 @@ belonging to the owner. Missing/invalid/expired → `401`. No anonymous access.
| `GET /api/lists` | web | → 200 `[{id,name}]` |
| `GET /api/lists/{id}/tasks` | web | → 200 tasks for the list; 404 if unknown |
| `POST /api/tasks` | web | Body `{title, description?, listId}`. Server-generated GUID, `source=web`, `consumed=false`. 404 if listId unknown. → 201 with the created task |
| `PUT /api/tasks/{id}` | desktop | Body `{listId, title, description?}`. Idempotent upsert (`source=desktop` on insert). → 201 (new) / 200 (existing) |
| `DELETE /api/tasks/{id}` | desktop | Idempotent. → 204 (even if absent) |
| `GET /api/tasks?consumed=false` | desktop | → 200 `[{id, listId, title, description, createdAt}]` web tasks not yet imported |
| `POST /api/tasks/{id}/consume` | desktop | Sets `consumed=true`. Idempotent. → 200; 404 if unknown |
| `PUT /api/tasks/mirror` | desktop | Body `[{id, listId, title, description?}, ...]` = the FULL current Idle backlog (camelCase; `[]` is valid). Full-replace of the desktop-owned partition: upsert each (as `consumed=true`), delete any `consumed=true` task not in the array, leave web-created `consumed=false` tasks untouched. Mirrors `PUT /lists`. → 200 |
| `GET /api/tasks?consumed=false` | desktop | → 200 `[{id, listId, title, description, createdAt}]` web tasks not yet imported (awaiting pull) |
| `POST /api/tasks/{id}/consume` | desktop | Sets `consumed=true` (imports a pulled web task into the desktop partition). Idempotent. → 200; 404 if unknown |
| `PUT /api/tasks/{id}` · `DELETE /api/tasks/{id}` | desktop | Legacy per-task upsert/delete — **superseded by `PUT /tasks/mirror`**. Kept for compatibility. |
Task ids are a **shared GUID space**: web-created ids are reused verbatim by the desktop on
import; all task writes are idempotent upserts keyed on `id`.
The `consumed` flag is the desktop-owned partition marker: `consumed=false` = web-created
awaiting pull; `consumed=true` = imported/desktop-owned (managed by the mirror). Task ids are
a **shared GUID space**: web-created ids are reused verbatim by the desktop on import; all
task writes are idempotent upserts keyed on `id`.
## Zitadel configuration (for the desktop client)