Commit Graph

71 Commits

Author SHA1 Message Date
Developer 10fd4ead4a feat(widgets): rebind widgets to the service registry
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.
2026-06-22 16:42:56 +00:00
Developer 8cdeadd6dd feat(services): backend service registry foundation (encryption, definitions, CRUD)
PR 1 of 4 for the runtime service registry change.

- Add Fernet encryption helper (services/secrets.py) with a required
  MANAGE_ENCRYPTION_KEY; validate it on startup.
- Add closed integrations/ registry with Pydantic config + widget-config
  definitions for grafana, prometheus, jellyfin, nextcloud, and ssh_tasks.
- Add services + service_task_runs tables and SettingsStore CRUD with
  cascade-delete (defensive until widgets carry service_id).
- Add /api/services/types and /api/services/instances CRUD (encrypted secrets,
  secrets_set flags only; never plaintext).
- Declare cryptography as a direct dependency.
- Require MANAGE_ENCRYPTION_KEY in compose + .env.example + README.
- Add 25 backend tests (registry, encryption, CRUD, cascade, task-run history).

Verification: ruff clean; pytest 225 passed; frontend lint/build green.
2026-06-22 12:56:03 +00:00
Developer 9459de5c07 docs(service-registry): lock decisions (cascade delete, required key, SSH runner model)
- §11 decisions: cascade-delete services with widgets; MANAGE_ENCRYPTION_KEY
  always required; SSH task runner is multi-instance with reusable tasks.
- §12 SSH task runner model: instances absorb SSH task transport, tasks stay
  global/reusable with default_service_id, service_task_runs logs history,
  widget config { task_id, service_id? }.
- tasks.md: add service_task_runs table + cascade-delete tests to Slice 1,
  SSH run-logging to Slice 2, follow-ups (Actions rebuild, machine unification).
2026-06-22 11:14:01 +00:00
Developer 9782280a03 docs(service-registry): SDD proposal, design, and tasks
Design-only artifacts for the runtime service registry change. No
implementation yet.

- proposal: motivation, goals, non-goals, grilling decisions, risks
- design: data model, Pydantic service definitions, encryption, API,
  frontend structure, migration/breaking changes, 4-PR slice plan
- tasks: backend foundation, backend widget rebind, frontend services
  runtime, dashboard + settings rework + docs
2026-06-22 10:07:25 +00:00
Developer ed7a7a5ce0 feat(widgets): dashboard loop, widget config UI, and addon pages
PR 4 of 4 for configurable dashboard widgets.

- Replace hard-coded Jellyfin/Backups dashboard sections with a loop that
  renders enabled widget instances by sort_order.
- Add WidgetInstance renderer and WidgetConfigDialog for adding, editing,
  enabling/disabling, deleting, and reordering widgets.
- Add addon pages for grafana, prometheus, and ssh-tasks at /addons/:addonId.
- Register /addons/:addonId route in App.tsx.
- Update docs/REQUIREMENTS.md with the widget system design and API.

Verification:
- backend ruff clean; pytest 200 passed
- frontend npm run lint: 0 errors
- frontend npm run build: success
- frontend npm run test -- src/widgets/registry.test.ts: 3 passed
2026-06-21 20:45:42 +00:00
Developer e1356b20f1 feat(widgets): add frontend widget runtime (types, API, hooks, registry, components)
PR 3 of 4 for configurable dashboard widgets.

- Add TypeScript widget interfaces (WidgetInstance, WidgetInstanceInput,
  WidgetTypeInfo, WidgetDataResponse).
- Create widget API client for CRUD, registry metadata, and per-widget data.
- Create TanStack Query hooks for instances, data, sources, types, and mutations.
- Create closed frontend widget registry with metadata, source type, refresh
  intervals, and config fields.
- Add six shadcn/ui-based widget components: Jellyfin, Backups, Grafana link,
  Prometheus metric, SSH task output, and static text.
- Add Vitest unit tests for registry metadata.

Verification:
- backend ruff clean; pytest 200 passed
- frontend npm run lint: 0 errors
- frontend npm run build: success
- frontend npm run test -- src/widgets/registry.test.ts: 3 passed
2026-06-21 16:24:44 +00:00
Developer 1cd8e926de feat(widgets): add backend source adapters and per-widget data endpoint
PR 2 of 4 for configurable dashboard widgets.

- Add grafana_url and prometheus_url settings (config.py + compose/env).
- Create WidgetSource protocol and adapters for jellyfin, backups, grafana,
  prometheus, ssh_task, and static sources.
- Add GET /api/widgets/instances/{id}/data endpoint.
- Extract shared dashboard helpers into domain/dashboard.py so widgets and
  the dashboard router reuse the same logic.
- Add adapter and data-endpoint tests.
- Update apply-progress.md.

Verification: ruff clean; backend pytest 200 passed; frontend lint/build green.
2026-06-21 10:09:45 +00:00
Developer 200d319fb0 feat(widgets): add backend CRUD, registry, and default seeding
Introduce a closed, compile-time widget registry and backend CRUD for
dashboard widget instances.

- Add dashboard_widgets SQLite table in SettingsStore with CRUD helpers and
  default seeding (Jellyfin + Backups) on first install.
- Add Pydantic models with credential-key and secret-value rejection.
- Add widgets router: /api/widgets/sources, /types, /instances CRUD.
- Call ensure_defaults() in app lifespan so fresh installs seed defaults.
- Add backend tests covering registry, CRUD, validation, and seeding.
- Include SDD artifacts: exploration, proposal, spec, design, tasks.
2026-06-19 20:07:47 +00:00
Developer 08a3b616f6 refactor(monitoring): decommission legacy SSH-scraping poller (slice 1)
The 2026-06-16/17 observability update externalised metrics to
Prometheus + node_exporter + Grafana, but the legacy Manage-side
SSH-scraping monitor was never removed. It duplicated the new stack,
ran SSH df on every machine every 300s, and fed nothing (its UI was
deleted in e2ad731). This slice decommissions the duplication.

Removed (backend):
- services/monitoring_poller.py (MonitoringPoller) — entire file
- services/monitoring_actions.py (disk_space, run_machine_operation,
  poll_machine_snapshot, build_machine_client) — entire file;
  run_machine_operation had only 2 callers (the poller + /disk), both gone
- tests/test_monitoring_actions.py
- endpoints: POST /api/monitoring/poller, GET /machines/{id}/actions,
  GET /disk (and the now-dead _resolve_machine helper)
- lifespan wiring (main.py), dependency wrapper (dependencies.py),
  poller.start()/kick() from machine save (routers/settings.py)
- SettingsStore: monitoring_machine_actions table CREATE + 2 indexes +
  record/list/prune_machine_actions methods; DROP TABLE IF EXISTS on
  startup cleans existing DBs (user-approved)
- config knobs: monitoring_poll_interval_seconds,
  monitoring_poll_initial_delay_seconds, monitoring_action_retention_days
- test_api.py: TestMonitoring._ensure_machine + test_disk

Kept (fits the new model): /machines, /prometheus-targets, /alerts,
/alertmanager-status, /alertmanager-webhook; the disk_usage JOB template
(manual on-demand, not monitoring); node_exporter_* machine fields
(they point Prometheus at the right host).

Gate: backend pytest 173 passed; ruff clean.
2026-06-17 20:48:56 +00:00
Developer 1c29299e8c chore(openspec): archive web-ui-rework change
Move the completed, verified, synced change folder to the dated archive:
openspec/changes/web-ui-rework/ -> openspec/changes/archive/2026-06-17-web-ui-rework/

Canonical spec stays in place at openspec/specs/web-ui/spec.md (not moved).
This is the physical archive step deferred (documented-pending-manual) in
archive-report.md ec46e26; user-approved.
2026-06-17 19:44:57 +00:00
Developer 9cae5fc98c chore(openspec): archive report — web-ui-rework lifecycle complete
Disposition: documented-pending-manual (archive-move deferred to avoid guessing
the OpenSpec archive convention destructively). Remaining manual step recorded:
move openspec/changes/web-ui-rework/ -> openspec/changes/archive/2026-06-17-web-ui-rework/
(canonical openspec/specs/web-ui/spec.md stays in place).

All 8 lifecycle phases done: proposal/spec/design/tasks/apply/verify/sync/archive.
71/71 tasks complete; gates green (build + lint + vitest 23/64 + node 5/5).

Carry-over follow-ups recorded for future cleanup:
- slice-5 shipped without the prescribed 5a/5b sub-split (correct + tested)
- tasks.md 'node --test tests' cross-check is a pre-existing broken command
  (correct: 'node --test'); recommend docs/npm-script follow-up
- no visual/browser smoke performed (component tests are structural)
2026-06-17 19:36:23 +00:00
Developer 7646f3236f chore(openspec): verify + sync reports, canonical web-ui spec (rework PASS)
Web UI rework lifecycle: verify + sync phases.
- verify-report.md: PASS verdict (23/64 vitest, 5/5 node, zero @mui in src,
  71/71 tasks, all spec scenarios green). Non-blocking findings recorded:
  slice-5 review-budget overage (5a/5b not split); node --test tests cmd typo.
- sync-report.md + openspec/specs/web-ui/spec.md: canonical domain spec
  distilled from the verified change (design system, thin-dashboard model,
  TanStack visibility-only tables, reconciled IA, removed deps, Vitest harness).
- Change-side delta spec under openspec/changes/web-ui-rework/specs/.
2026-06-17 19:15:15 +00:00
Developer 9de2d5b8d2 feat(frontend): slice 8 — remove MUI/@emotion deps + update REQUIREMENTS
Web UI rework. Final slice.
- Remove from package.json: @mui/material, @mui/icons-material,
  @mui/x-data-grid, @emotion/react, @emotion/styled (zero consumers
  remain in src after slices 1-7b; grep-verified).
- Update docs/REQUIREMENTS.md (+63 lines): single design system
  (shadcn/ui + Tailwind v4 + lucide-react), thin-dashboard observability
  model (no in-app charts; Grafana deep-links), TanStack tables
  (visibility-only parity), reconciled IA (Backups top-level nav;
  Media at /media with /applications redirect), repurposed chart-*
  status cues, removed deps list.
- Note: AGENTS.md 'node --test tests' invocation is a pre-existing
  broken command (treats tests/ as a module); correct form is
  'node --test' (auto-discover, 5/5 pass) — verified identical at
  pre-rework baseline ef5311b.

Gate: build + lint + vitest (23/64) + node --test (5/5) green.
Web UI rework complete.
2026-06-17 18:52:27 +00:00
Developer 3dc1b31fc3 feat(frontend): slice 7b — Media on TanStack Table (server pagination)
Web UI rework. Completes the DataGrid migration (7a + 7b):
- pages/Media.tsx off @mui/x-data-grid + @mui/material onto DataTable:
  15 locked columns (title/series/season/episode/type/year/runtime_min/
  size/bitrate/hdr/video/resolution/date_added/library/path);
  enablePagination + manualPagination + rowCount from queryResult.total;
  page state (pageIndex/pageSize) -> offset/limit into useMediaQuery;
  onRowClick -> navigate('/files?path=...') preserved; stable path-derived
  getRowId so selection survives server paging; column-visibility toggle.
  Hard rule honored: NO sorting, NO resizing (visibility-only).
- Migrate Media shell (Select/Input/Progress/Card/grid/Typography/Tabs).
- Media component tests (column set + row-click nav).
- Harness fix: polyfill ResizeObserver in test/setup.ts — jsdom lacks it
  and Radix primitives (Select/ScrollArea/etc.) reference it; was causing
  cross-test failures once Media pulled shadcn Select into the pool.

Gate: build + lint + test green (23 files / 64 tests).
2026-06-17 18:33:59 +00:00
Developer e8b0f1144b feat(frontend): slice 7a — DataTable wrapper + FileBrowser (TanStack Table)
Web UI rework. Highest-risk slice, part 1 of 2:
- New components/ui/data-table.tsx: generic TanStack Table wrapper on the
  shadcn Table primitive. Controlled rowSelection/columnVisibility/
  pagination, optional selection column (stopPropagation on cell click),
  row-click, column-visibility dropdown, manual-pagination support.
  Hard rule honored: NO getSortedRowModel, NO column resizing/sizing.
- Migrate pages/FileBrowser.impl.tsx off @mui/x-data-grid + @mui/material
  onto DataTable: 5 columns (type/name/ext/size/modified), row-click ->
  ffprobe preview preserved, column-visibility toggle, no pagination.
- DataTable + FileBrowser component tests (RED->GREEN).

Gate: build + lint + test green (22 files / 58 tests).
2026-06-17 18:02:47 +00:00
Developer 1e23c07a20 feat(frontend): slice 6a — Users directory surface + drawer (shadcn)
Web UI rework. Slice 6a (force-split; 6b = compose dialog next):
- UsersPage.impl.tsx directory surface off @mui: shadcn Table family +
  Checkbox + Badge (status: success=chart-2/destructive/secondary) +
  Avatar + Tooltip + Progress + Alert/Button/Stack/Typography
- MUI Drawer -> shadcn Sheet side="right" for user detail drawer
  (buildUserDrawerModel rendering preserved)
- Selection-across-pagination + search/filter parity preserved
- Compose-dialog MUI subset (Dialog/TextField/Divider/IconButton +
  9 icons) intentionally LEFT for slice 6b

Gate: build + lint + test green.
2026-06-17 14:36:07 +00:00
Developer b6da7df7f9 feat(frontend): slice 5 — migrate Settings + Actions to shadcn/Tailwind
Web UI rework. Form-heavy pair (controlled useState parity, no form lib):
- pages/Settings.tsx off @mui: monitoring-machine CRUD, SSH-key mgmt,
  SSH test/validation feedback, danger-zone reset (ConfirmDialog), tabs
- pages/Actions.tsx off @mui: saved-task editor, machine selection,
  run history, tabs
- Both reuse migrated shared components (SectionCard/SelectionRailCard/
  TabbedCard/HoverEditButton/ConfirmDialog/DialogFooter) as before
- Behavioral tests added (mocked hooks; no live SSH)

Gate: build + lint + test green (19 files / 39 tests).
2026-06-17 13:47:57 +00:00
Developer c721f0dece feat(frontend): slice 4 — migrate Dashboard + Applications to shadcn/Tailwind
Web UI rework.
- Migrate pages/Dashboard.tsx off @mui (shortcut CRUD dialogs, machine
  switcher, BackupDashboardWidget mount; shortcuts reuse ConfirmDialog/
  DialogFooter from slice 2)
- Migrate pages/Applications.tsx shell off @mui (tabs + library counts);
  keeps <Media/> child intact (Media.tsx still MUI, deferred to slice 7
  with its DataGrid)
- Behavioral tests for both pages

Gate: build + lint + test green.
2026-06-17 13:15:51 +00:00
Developer 77c6b62ee2 feat(frontend): slice 3 — Backups cluster migration + nav/IA
Web UI rework.
- Migrate BackupAlertsTable, BackupJobsTable, BackupRunsTable,
  BackupsPage, BackupDashboardWidget off @mui (shadcn Table + Badge
  severity variants: success=chart-2, warning=chart-3, destructive)
- App.tsx IA: Backups now top-level nav (DatabaseBackup icon);
  Media surface primary at /media; /applications -> /media redirect
  in both route trees (mirrors /monitoring -> /observability)

Gate: build + lint + test green.
2026-06-17 12:53:36 +00:00
Developer 109e74db41 feat(frontend): slice 2 — migrate 11 shared components to shadcn/Tailwind
Web UI rework. Shared-components slice (drift prevention):
- Migrate SectionCard, SelectionRailCard, TabbedCard, MetricCard,
  DiskSpaceCard, HoverEditButton, DialogFooter, ConfirmDialog,
  LibraryOverview, NowPlaying, SessionActivityPanel off @mui
- HoverEditButton: MUI IconButton + EditOutlined -> Button + lucide Pencil
- Status mapping uses the success Badge variant (chart-2) for healthy
- Exported APIs preserved so consuming pages still compile (no page edits)
- 11 behavioral Vitest component tests added

Gate: build + lint + test green.
2026-06-17 12:33:47 +00:00
Developer dd778d8850 feat(frontend): slice 1 — foundation for MUI->shadcn migration
Web UI rework (openspec/changes/web-ui-rework). Foundation slice:
- Add @tanstack/react-table; remove orphaned recharts, d3
- Vendor shadcn primitives: tabs table dialog input label checkbox
  switch progress separator avatar textarea dropdown-menu scroll-area
- Add Vitest + @testing-library/react + jsdom harness; npm test script
- Delete no-op theme.ts shim; remove getAppTheme references
- Smoke test proves the harness

Gate: build + lint + test green.
2026-06-17 12:11:05 +00:00