1da67f38c7
PR 3 of 4 for the runtime service registry change. - Add service + new-shape widget TypeScript types; widgets carry service_id + widget_kind (service-bound) or null (built-in). - Add services API client + TanStack Query hooks; reconcile the widget API client/hooks to the new endpoints (remove sources/types; add builtin kinds). - Add closed frontend service registry (integrations/registry.ts) mirroring the backend, with resolveWidget(widget, services) mapping a widget to its component + refresh interval. - Add ServicePage at /services/:serviceType/:serviceId with config view, empty-on-edit secret inputs + 'set' badges, enable toggle, delete, and the service's widget-kind list. - Register /services/:serviceType/:serviceId in App.tsx. - Reconcile the six widget components to refreshIntervalMs + description props; rewrite WidgetConfigDialog around a service -> widget-kind picker. - Update Dashboard test; add integrations/registry.test.ts. Verification: frontend lint 0 errors, build success, 70 tests passed; backend ruff clean, 222 tests passed.
88 lines
3.9 KiB
Markdown
88 lines
3.9 KiB
Markdown
# Apply Progress: Runtime Service Registry
|
||
|
||
**Change:** `service-registry`
|
||
**Apply run:** PR 1 + PR 2 + PR 3 (Slices 1–3)
|
||
**Date:** 2026-06-19
|
||
|
||
## Slice 1 — Backend service foundation (MERGED, PR #7)
|
||
|
||
Fernet secrets, closed `integrations/` registry (Pydantic config + widget-config
|
||
for grafana/prometheus/jellyfin/nextcloud/ssh_tasks), `services` +
|
||
`service_task_runs` tables with cascade delete, `/api/services*` CRUD,
|
||
`MANAGE_ENCRYPTION_KEY` required at startup.
|
||
|
||
## Slice 2 — Backend widget rebind (MERGED, PR #8)
|
||
|
||
Widgets carry `service_id` + `widget_kind`; adapters take
|
||
`fetch(service: ServiceRecord | None, widget_kind, config)`; backups + static
|
||
stay as service-less built-ins; SSH adapter logs to `service_task_runs`; old
|
||
`widgets/registry.py` retired; default seeding removed.
|
||
|
||
## Slice 3 — Frontend services runtime (this PR)
|
||
|
||
### Completed tasks
|
||
|
||
- [x] 3.1 Service + new widget TypeScript types (`ServiceInstance`,
|
||
`ServiceInstanceInput`, `ServiceTypeInfo`, `ServiceWidgetKindInfo`,
|
||
`SecretFieldInfo`, `BuiltinWidgetKindInfo`; widget gains `service_id` +
|
||
`widget_kind`).
|
||
- [x] 3.2 Services API + hooks (`api/services.ts`, `hooks/useServices.ts`).
|
||
Reconciled `api/widgets.ts` + `hooks/useWidgets.ts` to the new shape
|
||
(removed sources/types; added builtin kinds).
|
||
- [x] 3.3 Closed frontend service registry (`integrations/registry.ts`)
|
||
mirroring the backend; `resolveWidget(widget, services)` maps a widget to
|
||
its component + refresh interval.
|
||
- [x] 3.4 Service page at `/services/:serviceType/:serviceId` with config view,
|
||
empty-on-edit secret inputs + "set" badges, enable toggle, delete, and the
|
||
service's widget-kind list.
|
||
- [x] 3.5 Route swap: added `/services/:serviceType/:serviceId`; addon route
|
||
retained for now (removed in Slice 4 cleanup).
|
||
- [x] 3.6 Reconciled widget components to take `refreshIntervalMs` +
|
||
`description` props; rewrote `WidgetConfigDialog` around the
|
||
service → widget-kind picker (pulled 4.1 forward to keep the build whole).
|
||
- [x] 3.7 Registry + Dashboard tests updated; new
|
||
`integrations/registry.test.ts`.
|
||
|
||
### Decision resolved mid-slice
|
||
|
||
Secret edit UX = **empty-on-edit + "set" badge** (blank = keep existing; typing
|
||
= replace). Applied on the ServicePage secrets card.
|
||
|
||
### Files changed (Slice 3)
|
||
|
||
- New: `api/services.ts`, `hooks/useServices.ts`, `integrations/registry.ts`,
|
||
`integrations/registry.test.ts`, `pages/ServicePage.tsx`.
|
||
- Modified: `types/index.ts`, `api/widgets.ts`, `hooks/useWidgets.ts`,
|
||
`components/WidgetInstance.tsx`, `components/WidgetConfigDialog.tsx`,
|
||
`pages/Dashboard.tsx`, `pages/__tests__/Dashboard.test.tsx`, `App.tsx`,
|
||
all six `widgets/*.tsx` components, `widgets/index.ts`.
|
||
- Deleted: `widgets/registry.ts`, `widgets/registry.test.ts`.
|
||
|
||
### Verification (Slice 3)
|
||
|
||
```bash
|
||
cd frontend
|
||
npm run lint # 0 errors
|
||
npm run build # success
|
||
npm run test # 70 passed
|
||
cd ../backend
|
||
.venv/bin/ruff check . # clean
|
||
PYTHONPATH=src .venv/bin/python -m pytest # 222 passed
|
||
```
|
||
|
||
### Deviations / notes
|
||
|
||
- `WidgetConfigDialog` was rewritten in this slice (pulled forward from task
|
||
4.1) because the old dialog imported the deleted widget registry and would
|
||
not compile. The SSH task-output widget keeps a dedicated task picker; other
|
||
widget configs use a generic schema-driven field editor.
|
||
- Addon pages (`/addons/:addonId`) are kept compiling but superseded by service
|
||
pages; Slice 4 removes them and the now-unused machine Jellyfin/Jellyseerr
|
||
fields + `grafana_url`/`prometheus_url` env vars, and writes the changelog.
|
||
|
||
## Remaining work
|
||
|
||
- Slice 4: remove addon pages + machine app fields, remove
|
||
`grafana_url`/`prometheus_url` from config + compose, docs + changelog
|
||
(breaking upgrade note).
|