Implement POST /encode endpoint for encrypting user configuration tokens.
Uses Fernet encryption with automatic Base64 fallback when CONFIG_SECRET_KEY
is not set. Protected by signed CSRF token validation to prevent cross-site
attacks.
- Implement Fernet encryption for configuration URLs to protect sensitive user settings
- Add CONFIG_SECRET_KEY environment variable for key derivation
- Add server-side /api/config/encode endpoint for secure config token generation
- Add CSRF token generation and validation for all admin POST routes
- Auto-inject CSRF tokens into admin forms and AJAX requests via JavaScript
- Update frontend to use async server encoding instead of client-side Base64
- Add localStorage fallback for config restoration on /configure pages
- Maintain backward compatibility with existing Base64-encoded URLs
- Log warning at startup if CONFIG_SECRET_KEY is not configured
- Convert debug logs to trace level in TorrentSmartContainer for reduced noise
Introduce a metadata_mappings table with DAO/model layers for admin-managed
IMDB→TMDB/title overrides, and overhaul the admin panel with a modern
Bootstrap 5 sidebar UI.
- Add metadata_mappings table with migrations (search_titles, year_override)
- Add MetadataMappingDAO with standalone session helpers for use outside FastAPI
- Add new admin pages: dashboard, mappings CRUD, maintenance, config, hash/TMDB search
- Integrate DB mapping lookups into Cinemeta and TMDB metadata providers
- Update catalog views to use DB mappings before hitting external APIs
- Rewrite all admin templates with Bootstrap 5, sidebar navigation, French labels
- Add session-based auth for admin routes with Redis session store
Introduces a secure peer-to-peer cache sharing system using HMAC-SHA256 request signatures and Fernet (AES-128-CBC) response encryption.
- New peer_keys table for per-peer credential management (key_id, secret)
- New /api/peer/check endpoint returns encrypted debrid availability data
- New /api/peer/items endpoint returns encrypted torrent metadata
- Rate limiting per peer key with configurable limits and windows
- Admin UI for creating, viewing, revoking, and deleting peer keys
- Peer cache enrichment integrated into BaseDebrid L2.5 lookup pipeline
- StremThruDebrid override with cross-service PG L2 check + peer cache
- Removed old /api/share/cache endpoint (replaced by /api/peer/*)
- Added cryptography package dependency
BREAKING CHANGE: The /api/share/cache endpoint has been removed and replaced with /api/peer/check. Clients must update to use HMAC authentication with X-Peer-Key-Id, X-Peer-Timestamp, and X-Peer-Signature headers.
Add new `/share/cache` API endpoint that returns which info-hashes are confirmed cached on a given debrid service. Uses hash-only projection to avoid leaking file metadata. Includes API key auth and Redis-based rate limiting.
Async correctness:
- Wrap blocking time.sleep/requests.get calls with asyncio.to_thread()
in torrent_service.py and torr9_service.py
- Run cache_container_items() as fire-and-forget background task via
asyncio.create_task(asyncio.to_thread(...)) in search/views.py
- Make RDTokenManager.get_access_token() and new_access_token() async,
replace requests.post with aiohttp (token_manager.py)
- Make RealDebrid.get_headers() async, await all callers (realdebrid.py)
Security:
- Remove API key, device code, and PIN values from log output
(auth/views.py, admin/views.py)
- Add WARNING on startup when SESSION_KEY uses the insecure default
value (settings.py)
- Make admin template dir configurable via settings.admin_template_dir
Performance:
- Parallelize TMDB metadata fetches with asyncio.gather() (catalog/views.py)
- Add tmdb_language setting instead of hardcoded "fr-FR"
Deduplication:
- Extract ensure_uuid() to stream_fusion/web/api/utils.py
- Extract get_client_ip() to stream_fusion/web/utils.py
- Replace 8-way if/elif debrid factory with _SERVICE_MAP dict +
_build_service() helper (get_debrid_service.py)
- Extract _model_to_schema() in APIKeyDAO to remove 6 repeated blocks
- Simplify ProxyStreamer: remove manual BytesIO buffer, stream directly
via iter_any() (stream/views.py)
Inspired by commits 823bc77 and e306eb5 from limedrive/develop.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>