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.
3.5 KiB
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
- 2.1 Add
service_id/widget_kindcolumns todashboard_widgets(additive ALTER; legacyaddon_id/widget_typekept but unused). - 2.2 Refactor source adapters to
fetch(service, widget_kind, config)withServiceRecord | None.SERVICE_ADAPTERSkeyed by service_type;BUILTIN_ADAPTERSfor backups/static. SSH adapter resolves the task + instance, runs, and appends aservice_task_runsrow (success/failure/ timeout/error). - 2.3 Retire old
widgets/registry.py(deleted; metadata now comes fromintegrations/registry+widgets/builtin). - 2.4 Update widgets router + models for service-bound + built-in widgets.
Removed
/api/widgets/typesand/api/widgets/sources; added/api/widgets/builtin. - 2.5 Rewrite widget tests around the new model.
- 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_modelfor Pydantic widget-config validation). - Rewritten:
tests/test_widgets.py(26 tests).
Verification (Slice 2)
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_urlenv vars, docs + changelog.