feat(setup): one-call setup scaffold + onboarding flag (phase 2)
Backend for the first-run wizard (phase 4).
- POST /api/v1/setup/scaffold: given an existing device_id + display_index
(+ optional calibration), wires a working chain via the real validated
store create paths — create-or-reuse capture template -> raw picture
source -> picture color-strip source (calibration or default) -> LED
output target -> returns the ids. Does NOT auto-start. Rolls back every
entity it created (reverse order) on any partial failure, leaving no
orphans; "created" events are deferred until the whole chain succeeds so
a rolled-back scaffold never leaves ghost cards in the UI.
- Requires an existing device_id (no inline device creation) — the wizard
creates the device first via the canonical, URL-validated POST /devices,
so the scaffold can't bypass device validation. display_index is bounded.
- GET/PUT /api/v1/preferences/onboarding: persistent first-run flag
({onboarded, completed_at}) via db.set_setting; server stamps completed_at.
- Both routes AuthRequired. Tests: 25 (scaffold happy/reuse/rollback/
validation + onboarding + calibration round-trip integration). docs/API.md.
Part of the edge-calibration + first-run-wizard feature (Big Bang; intermediate
phase — full build/suite gated at the final phase).
This commit is contained in:
+68
-1
@@ -185,7 +185,7 @@ Server configuration: MQTT broker, external URL, shutdown action, log level, ADB
|
||||
|
||||
## User preferences
|
||||
|
||||
Dashboard layout, notification settings, card display modes, and the global daylight timezone.
|
||||
Dashboard layout, notification settings, card display modes, the global daylight timezone, and the first-run onboarding flag.
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ---- | ----------- |
|
||||
@@ -199,6 +199,19 @@ Dashboard layout, notification settings, card display modes, and the global dayl
|
||||
| DELETE | `/api/v1/preferences/card-modes` | Delete card-mode preferences; revert to defaults. |
|
||||
| GET | `/api/v1/preferences/daylight-timezone` | Read the global IANA timezone for daylight cycles. |
|
||||
| PUT | `/api/v1/preferences/daylight-timezone` | Persist the daylight-cycle timezone (empty = server local). |
|
||||
| GET | `/api/v1/preferences/onboarding` | Read the first-run onboarding flag (`onboarded: bool`, `completed_at: str\|null`). Defaults to `false`. |
|
||||
| PUT | `/api/v1/preferences/onboarding` | Persist the onboarding flag. Server auto-stamps `completed_at` when `onboarded` is set to `true` without a timestamp. |
|
||||
|
||||
**Onboarding flag response shape:**
|
||||
|
||||
```json
|
||||
{
|
||||
"onboarded": true,
|
||||
"completed_at": "2026-06-08T12:00:00.000000+00:00"
|
||||
}
|
||||
```
|
||||
|
||||
Defaults to `{"onboarded": false, "completed_at": null}` when never set.
|
||||
|
||||
## Backup, restore & server control
|
||||
|
||||
@@ -716,6 +729,60 @@ strip-walk order defined by `(start_position, layout)`. Provide either
|
||||
**Idle timeout:** a session that receives no `position` calls for 60 seconds
|
||||
is automatically stopped and the prior target restored.
|
||||
|
||||
## Setup scaffold
|
||||
|
||||
One-call first-run helper that creates the full capture-to-output chain and
|
||||
returns all entity ids. The wizard calls this, then starts the output target
|
||||
after optional calibration.
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ---- | ----------- |
|
||||
| POST | `/api/v1/setup/scaffold` | Create capture template + picture source + color-strip source + LED output target in one atomic call with rollback on partial failure. Does NOT auto-start the target. |
|
||||
|
||||
**Wizard sequence (Phase 4):**
|
||||
|
||||
1. Discover or create the device via `POST /api/v1/devices` (full URL
|
||||
normalisation + provider validation runs there).
|
||||
2. Call `POST /api/v1/setup/scaffold` with the resulting `device_id`.
|
||||
3. Calibrate (Phase 1 endpoints).
|
||||
4. Start the output target via `POST /api/v1/output-targets/{id}/start`.
|
||||
|
||||
**Request body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"device_id": "device_abc123",
|
||||
"display_index": 0,
|
||||
"calibration": null
|
||||
}
|
||||
```
|
||||
|
||||
`device_id` is **required** and must reference an existing device (created via
|
||||
`POST /api/v1/devices`). `display_index` selects the monitor to capture
|
||||
(0 = primary; range 0–63). `calibration` is an optional `CalibrationConfig`
|
||||
dict; when omitted, `create_default_calibration(led_count)` is used.
|
||||
|
||||
**Response (201 Created):**
|
||||
|
||||
```json
|
||||
{
|
||||
"device_id": "device_abc123",
|
||||
"capture_template_id": "tpl_11223344",
|
||||
"picture_source_id": "ps_aabbccdd",
|
||||
"color_strip_source_id": "css_11223344",
|
||||
"output_target_id": "pt_aabbccdd",
|
||||
"capture_template_reused": true
|
||||
}
|
||||
```
|
||||
|
||||
`capture_template_reused` is `true` when an existing template matched the
|
||||
platform engine (no new template was created).
|
||||
|
||||
**Rollback:** if any step fails, all entities created within the same call are
|
||||
deleted in reverse order so no orphans remain. The pre-existing device and any
|
||||
reused template are never deleted. Entity "created" events are emitted only
|
||||
after the full chain succeeds, so a rollback never produces ghost UI cards.
|
||||
|
||||
## Web UI & PWA
|
||||
|
||||
App-level routes served by FastAPI (not under `/api/v1`).
|
||||
|
||||
Reference in New Issue
Block a user