10fd4ead4a
PR 2 of 4 for the runtime service registry change. - dashboard_widgets gains service_id + widget_kind columns (legacy addon_id/widget_type kept but unused). - Source adapters take (service: ServiceRecord | None, widget_kind, config). SERVICE_ADAPTERS keyed by service_type; BUILTIN_ADAPTERS for backups/static. - Backups and static stay as service-less built-ins (service_id nullable), exposed via GET /api/widgets/builtin. - SSH task adapter resolves the task + instance, runs over SSH, and appends a service_task_runs history row on success/failure/timeout/error. - Retire widgets/registry.py; widget metadata now comes from the integrations registry + widgets/builtin. Remove /api/widgets/types and /api/widgets/sources. - Stop default widget seeding (fresh install = empty dashboard). - Rewrite widget tests around the service-bound + built-in model (26 tests). Backend-only breaking change; frontend is reconciled in Slice 3. Build/lint stay green; pytest 222 passed.
79 lines
3.5 KiB
Markdown
79 lines
3.5 KiB
Markdown
# Apply Progress: Runtime Service Registry
|
|
|
|
**Change:** `service-registry`
|
|
**Apply run:** PR 1 + PR 2 / Slice 1 + Slice 2
|
|
**Date:** 2026-06-19
|
|
|
|
## Slice 1 — Backend service foundation (MERGED)
|
|
|
|
Completed in PR #7. See git history. Summary: Fernet secrets helper, closed
|
|
`integrations/` registry with Pydantic config + widget-config definitions for
|
|
grafana/prometheus/jellyfin/nextcloud/ssh_tasks, `services` + `service_task_runs`
|
|
tables with cascade delete, `/api/services*` CRUD, `MANAGE_ENCRYPTION_KEY`
|
|
required at startup, 25 tests.
|
|
|
|
## Slice 2 — Backend widget rebind to services (this PR)
|
|
|
|
### Completed tasks
|
|
|
|
- [x] 2.1 Add `service_id` / `widget_kind` columns to `dashboard_widgets`
|
|
(additive ALTER; legacy `addon_id`/`widget_type` kept but unused).
|
|
- [x] 2.2 Refactor source adapters to `fetch(service, widget_kind, config)`
|
|
with `ServiceRecord | None`. `SERVICE_ADAPTERS` keyed by service_type;
|
|
`BUILTIN_ADAPTERS` for backups/static. SSH adapter resolves the task +
|
|
instance, runs, and appends a `service_task_runs` row (success/failure/
|
|
timeout/error).
|
|
- [x] 2.3 Retire old `widgets/registry.py` (deleted; metadata now comes from
|
|
`integrations/registry` + `widgets/builtin`).
|
|
- [x] 2.4 Update widgets router + models for service-bound + built-in widgets.
|
|
Removed `/api/widgets/types` and `/api/widgets/sources`; added
|
|
`/api/widgets/builtin`.
|
|
- [x] 2.5 Rewrite widget tests around the new model.
|
|
- [x] 2.6 Stop default widget seeding (fresh install = empty dashboard).
|
|
|
|
### Decision resolved mid-slice
|
|
|
|
Backups and static widgets stay as **service-less built-ins** (`service_id`
|
|
nullable), per product decision. The data endpoint resolves built-ins via
|
|
`BUILTIN_ADAPTERS` and service-bound widgets via `SERVICE_ADAPTERS` + a
|
|
decrypted `ServiceRecord`.
|
|
|
|
### Files changed (Slice 2)
|
|
|
|
- New: `widgets/builtin.py` (built-in kinds + light config validation).
|
|
- Rewritten: `widgets/sources.py` (`ServiceRecord`, new protocol, service +
|
|
built-in adapters, SSH run logging, `_build_ssh_client`).
|
|
- Deleted: `widgets/registry.py`.
|
|
- Modified: `models/widgets.py` (service_id + widget_kind; `BuiltinWidgetKindInfo`).
|
|
- Modified: `routers/widgets.py` (new validation, `/builtin`, data resolution).
|
|
- Modified: `services/settings_store.py` (widget columns; no-op seeding).
|
|
- Modified: `integrations/base.py` (`WidgetKind.config_model` for Pydantic
|
|
widget-config validation).
|
|
- Rewritten: `tests/test_widgets.py` (26 tests).
|
|
|
|
### Verification (Slice 2)
|
|
|
|
```bash
|
|
cd backend
|
|
.venv/bin/ruff check . # All checks passed
|
|
PYTHONPATH=src .venv/bin/python -m pytest # 222 passed
|
|
cd ../frontend
|
|
npm run lint # 0 errors
|
|
npm run build # success
|
|
```
|
|
|
|
### Known transient state (resolved by Slice 3)
|
|
|
|
Slice 2 is a backend-only breaking change to the widget API. Until Slice 3
|
|
lands, the frontend still calls the removed `/api/widgets/types` and
|
|
`/api/widgets/sources` endpoints and uses the old `widget_type` shape, so the
|
|
dashboard widget config UI is non-functional at runtime. Build/lint stay green.
|
|
This is the accepted transient state for a stacked backend→frontend rebind.
|
|
|
|
## Remaining work
|
|
|
|
- Slice 3: Frontend services runtime (types, API, hooks, frontend service
|
|
registry, service pages, route swap, remove addon pages, reconcile widget UI).
|
|
- Slice 4: Dashboard picker on services, settings rework, remove
|
|
`grafana_url`/`prometheus_url` env vars, docs + changelog.
|