e25240c2f3
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.