a8dfbd5dc6
Delete the old top-level page files whose content was migrated into service-page tabs in slices 5-9: - pages/Media.tsx, Applications.tsx (-> MediaTab) - pages/FileBrowser.tsx, FileBrowser.impl.tsx (-> FilesTab) - pages/Actions.tsx (-> ActionsTab) - pages/Users.tsx, UsersPage.impl.tsx (replaced by Authentik tabs) - components/BackupsPage.tsx (-> JobsTab) - components/ObservabilityPage.tsx (split into Alerts/Links/Metrics tabs) - hooks/useUsers.ts (orphaned after Users page deletion) - the corresponding page test files (Media, FileBrowser, Applications, Actions, UsersPage) that tested the deleted pages directly. The service-tab components are the live implementations; ServicePage renders them. No live code references the deleted files. Docs: append an Information Architecture section to REQUIREMENTS.md documenting the services-as-hub model (nav shape, service-page tabs, service type registry, Users->Authentik, Observability split, legacy route 404s, empty state). Add a CHANGELOG entry under [Unreleased]. 92 frontend tests pass (was 112; -20 deleted page tests); 271 backend tests pass; lint/build green. Refs openspec/changes/services-as-hub-ia/ (tasks slice 11).
155 lines
8.1 KiB
Markdown
155 lines
8.1 KiB
Markdown
# Changelog
|
|
|
|
All notable changes to Manage. Breaking changes are marked with **BREAKING**.
|
|
|
|
## [Unreleased]
|
|
|
|
### Added — Services-as-hub IA rework
|
|
|
|
- **BREAKING:** Top-level navigation reorganized around services as the hub.
|
|
The always-visible core is Main Dashboard, Services, Settings. Conditional
|
|
per-type entries (Media, Files, Actions, Alerts, Grafana, Prometheus,
|
|
Backups, Users) appear only when a matching service is configured. Legacy
|
|
top-level routes (`/media`, `/files`, `/actions`, `/users`, `/observability`,
|
|
`/backups`) now return 404.
|
|
- **NEW service types:** `backups` (modeled as a service; reports attribute
|
|
first-wins to an enabled instance via `?service_id=`) and `authentik`
|
|
(user-directory source; replaces the Jellyfin-backed Users page).
|
|
- **Jellyseerr absorbed** into Jellyfin config (optional `jellyseerr_url` /
|
|
`jellyseerr_api_key`). Existing Jellyseerr service instances are migrated
|
|
into their paired Jellyfin at startup; unpaired instances are dropped with
|
|
a logged warning.
|
|
- **Service pages** now use a tab skeleton `[Overview | content tabs | Widgets |
|
|
Config]`. Operational content (Media, Files, Actions, Backups, Users,
|
|
Messaging, Alerts, Links, Metrics) lives in per-type tabs. An instance
|
|
switcher appears when >1 enabled instance of a type exists.
|
|
- **Named dashboards** at `/d/:slug` — user-created top-level entries composed
|
|
of pinned service links (full widget composition is a follow-up).
|
|
- **Authentik directory endpoint:** `GET /api/services/authentik/{id}/users`
|
|
(paginated, searchable). `POST .../message` enqueues emails via the existing
|
|
SMTP/mail queue.
|
|
- **Users router removed** (Jellyfin-backed directory + Jellyfin-email compose).
|
|
|
|
### Added — Observability service registry
|
|
|
|
- **Alertmanager is now a service type.** Configure Alertmanager, Grafana, and
|
|
Prometheus instances in the UI on the Services page; all three are first-class
|
|
service-registry entries with dashboard widgets (`active_alerts`, Grafana link,
|
|
Prometheus metric).
|
|
- New monitoring endpoints resolve the configured service instance and probe its
|
|
health: `GET /api/monitoring/grafana-status`, `/prometheus-status`. The
|
|
`/alerts` and `/alertmanager-status` endpoints now take an optional
|
|
`service_id` and pick the first enabled alertmanager instance by default.
|
|
- The Observability page discovers Grafana/Prometheus/Alertmanager from the
|
|
registry and renders health cards; the dashboard `active_alerts` widget sums
|
|
firing alerts by severity.
|
|
|
|
### Changed — Observability is now external only
|
|
|
|
- **Removed** all observability services from `docker-compose.yml` and
|
|
`docker-compose.dev.yml`. They now deploy **only** the backend and frontend.
|
|
The `monitoring` network and the `prometheus`/`loki`/`alloy`/`grafana`/
|
|
`alertmanager`/`node-exporter` services and their named volumes were deleted,
|
|
and the `GRAFANA_APP_HOST` Traefik rule was removed.
|
|
- Manage now connects to **existing** Grafana/Prometheus/Alertmanager instances
|
|
and never ships its own stack. The previous in-compose stack is preserved as
|
|
an optional, deploy-it-yourself example in `docker-compose.observability.yml`
|
|
(config under `monitoring/`, documented in `docs/observability-runbooks.md`).
|
|
- Removed the now-orphaned combined `monitoring/prometheus/prometheus.yml`; the
|
|
standalone stack uses `monitoring/prometheus/prometheus.standalone.yml`.
|
|
- Removed the Prometheus file-SD bridge (`PROMETHEUS_FILE_SD_DIR` + the
|
|
`write_prometheus_targets` file writer). External Prometheus instances now
|
|
consume node-exporter targets via `http_sd_configs` against
|
|
`GET /api/monitoring/prometheus-targets`. The webhook receiver is log-only.
|
|
|
|
### **BREAKING**
|
|
|
|
- Observability is configured entirely via the service registry; the backend
|
|
`alertmanager_url`/`alertmanager_webhook_url` and frontend
|
|
`VITE_GRAFANA_URL`/`VITE_PROMETHEUS_URL` environment variables, plus
|
|
`PROMETHEUS_FILE_SD_DIR`, were **removed**. Re-create your Alertmanager /
|
|
Grafana / Prometheus instances on the Services page after upgrading. The only
|
|
observability env var remaining is `PROMETHEUS_ENABLED` (toggles Manage's own
|
|
`/metrics` endpoint).
|
|
|
|
### Added — Service registry
|
|
|
|
- Runtime **service registry** persisted in the backend SQLite database. External
|
|
services (Grafana, Prometheus, Jellyfin, Nextcloud, SSH task runner) are now
|
|
configured in the app instead of via environment variables.
|
|
- Services page (`/services`) to create, list, and delete service instances.
|
|
- Service detail pages (`/services/:serviceType/:serviceId`) to edit name/enabled
|
|
state, rotate secrets, and view the widgets a service provides.
|
|
- Service definitions live as Pydantic modules in `backend/.../integrations/`,
|
|
each declaring its config schema, secret fields, and widget kinds.
|
|
- Multi-instance support: multiple Grafana/Jellyfin/etc. instances per type.
|
|
- SSH task runner service records run history in a new `service_task_runs`
|
|
table, shown on the runner's service page.
|
|
|
|
### Changed
|
|
|
|
- Dashboard widgets are now **service-bound** (reference a service instance +
|
|
widget kind) or **built-in** (backups, static text). The "Add widget" flow is
|
|
pick-service → pick-widget-kind → configure.
|
|
- Deleting a service cascade-deletes widgets that reference it.
|
|
|
|
### Security
|
|
|
|
- Service secrets (API keys, tokens, passphrases) are **encrypted at rest** with
|
|
Fernet.
|
|
|
|
### **BREAKING**
|
|
|
|
- Saved Actions (server tasks) now target `ssh_tasks` service instances instead
|
|
of monitoring machines. The `default_machine_id` field on saved tasks was
|
|
replaced with `default_service_id`; the legacy `saved_task_runs` table was
|
|
dropped and run history now lives in `service_task_runs`. Re-create SSH task
|
|
runner services on the Services page and re-link saved actions after
|
|
upgrading.
|
|
- **`MANAGE_ENCRYPTION_KEY` is now required** to start the backend. Generate one
|
|
with:
|
|
|
|
```bash
|
|
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
|
```
|
|
|
|
- The `GRAFANA_URL` and `PROMETHEUS_URL` backend environment variables were
|
|
removed; Grafana/Prometheus URLs now live on service records configured in the
|
|
UI. Re-create them on the Services page after upgrading.
|
|
- The legacy widget/addon-pages model (`/addons/:addonId`,
|
|
`/api/widgets/types`, `/api/widgets/sources`) was removed in favor of the
|
|
service registry.
|
|
- Default dashboard widget seeding was removed; a fresh install starts with an
|
|
empty dashboard. Add widgets from the dashboard's edit dialog after
|
|
configuring services.
|
|
|
|
### Notes / follow-ups
|
|
|
|
- ~~Machine-level Jellyfin/Jellyseerr app config still powers the Media/Users/Files
|
|
pages. Migrating those onto the service registry is a separate follow-up change.~~
|
|
**Done (2026-06-23):** Jellyfin is no longer a machine service, and the dead
|
|
machine-level `media_root`/`path_prefix` fields were removed. See the
|
|
Jellyfin migration entry in `docs/REQUIREMENTS.md`.
|
|
|
|
## Follow-up #2 — remove dead machine `media_root`/`path_prefix` + Jellyfin service
|
|
|
|
Completes the Jellyfin migration onto the service registry. Jellyfin is no
|
|
longer a machine `services` tag (`DEFAULT_SERVICES` is now `["monitoring",
|
|
"files"]`), and the dead machine-level `media_root`/`path_prefix` fields were
|
|
removed from the settings store, `MonitoringMachineInput`, frontend types, and
|
|
the Settings UI. Jellyfin is configured exclusively as a service-registry
|
|
instance. The global `REMOTE_MEDIA_ROOT`/`REMOTE_PATH_PREFIX` config properties
|
|
and `path_utils.py` remain (files/media-index still use them for Jellyfin→SSH
|
|
path resolution). Existing DB rows may still carry these keys in `config_json`;
|
|
they are inert and get dropped on the next machine save.
|
|
|
|
## Follow-up #1 — remove dead machine Jellyfin/Jellyseerr fields
|
|
|
|
With Jellyfin/Jellyseerr now resolved from the service registry, the machine-level
|
|
Jellyfin/Jellyseerr fields are dead config. Removed from `dependencies.py` (dead
|
|
`_jellyseerr_client_for`; `_resolve_machine` simplified to SSH-only),
|
|
`services/settings_store.py`, `routers/settings.py` (`MachineInput`), frontend
|
|
types, the `Settings.tsx` form, and frontend test fixtures. Existing DB rows may
|
|
still carry these keys in `config_json`; they are inert and get dropped on the
|
|
next machine save. No data migration required.
|