feat(jellyseer): move jellyseerr_api_key to an encrypted secret (slice 2/3)

The Jellyseerr API key was stored as plaintext in the Jellyfin service config.
It is now a SecretField on the Jellyfin service, so it is encrypted at rest and
rendered as a masked secret input (the generic config editor stops exposing
it, and the secret editor picks it up automatically).

Migration (idempotent, runs in ensure_defaults):
- _migrate_jellyseerr_api_key_to_secret: for every Jellyfin service with a
  plaintext jellyseerr_api_key still in config, encrypt it ONCE into the
  secrets blob (direct UPDATE so existing encrypted secrets are preserved, not
  re-encrypted) and remove it from config.
- _migrate_jellyseerr_into_jellyfin: standalone-jellyseerr absorption now
  stores the key as a secret, and decrypts the Jellyfin api_key before handing
  it to upsert_service (fixes a pre-existing double-encrypt on that rare path).

The stats provider already reads jellyseerr_api_key from secrets-or-config, so
it works before, during, and after the migration.

Tests: absorbed-key lands in secrets (and existing api_key isn't corrupted);
new plaintext-config -> secret migration + idempotency. 401/401 backend pass.
This commit is contained in:
Developer
2026-07-12 13:40:56 +00:00
parent b8cb29e330
commit e25240c2f3
7 changed files with 107 additions and 20 deletions
@@ -2,7 +2,7 @@
dir: backend/src/media_library_viewer_api/services
## role
Backend service layer providing business logic for media indexing, backup monitoring, email delivery, secrets management, task execution, and persistent storage operations.
Backend service layer providing business logic for backup monitoring, media indexing, email delivery, task execution, secrets management, and persistent data storage.
## parent
index: backend/src/media_library_viewer_api/.pi-map.index.md
map: backend/src/media_library_viewer_api/.pi-map.md