fix: sécuriser watchlist, favorites, downloads et recommendations sans auth (#15)

- router_favorites.py: toutes les routes requièrent maintenant l'auth
  - GET utilise get_optional_user + login_prompt.html pour HTMX
  - POST/DELETE/toggle requièrent get_current_user_from_token
  - Filtrage par user_id dans toutes les requêtes favorites
- router_downloads.py: GET list et GET status protégés (401 sans token)
- router_recommendations.py: GET protégé (login_prompt HTMX, 401 JSON)
- router_sonarr.py: tous les endpoints de gestion protégés
  - Webhooks restent publics (reçus de Sonarr)
- app/favorites.py: ajout du paramètre user_id à toutes les méthodes

Closes #15
This commit is contained in:
root
2026-04-02 22:20:29 +00:00
parent c0f9c0c1c4
commit 5d264d8f3b
5 changed files with 154 additions and 45 deletions
+20 -6
View File
@@ -2,13 +2,15 @@
Download management routes for Ohm Stream Downloader API.
"""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
from app.download_manager import DownloadManager
from app.models import DownloadRequest
from app.routers.router_auth import get_current_user_from_token
from app.models.auth import User
from app.routers.router_auth import get_current_user_from_token, get_optional_user
router = APIRouter(prefix="/api/downloads", tags=["downloads"])
templates = Jinja2Templates(directory="templates")
@@ -24,20 +26,28 @@ async def get_downloads(
request: Request,
html: bool = Query(False),
download_manager: DownloadManager = Depends(get_download_manager),
current_user: Optional[User] = Depends(get_optional_user),
):
"""Get list of all download tasks. Returns HTML for HTMX requests."""
tasks = download_manager.get_all_tasks()
# Strictly check for HTMX or explicit HTML flag
is_htmx = request.headers.get("HX-Request") == "true" or request.headers.get("HX-Request")
if current_user is None and (html or is_htmx):
return templates.TemplateResponse(
"components/login_prompt.html", {"request": request}
)
if current_user is None:
raise HTTPException(status_code=401, detail="Authentication required")
tasks = download_manager.get_all_tasks()
if html or is_htmx:
print(f"[DOWNLOADS] HTML Request. Found {len(tasks)} tasks.")
return templates.TemplateResponse(
"components/downloads_list.html",
{"request": request, "tasks": tasks}
)
print(f"[DOWNLOADS] API Request. Returning JSON.")
return {"downloads": tasks}
@@ -56,8 +66,12 @@ async def create_download(
async def get_download_status(
task_id: str,
download_manager: DownloadManager = Depends(get_download_manager),
current_user: Optional[User] = Depends(get_optional_user),
):
"""Get status of a specific download task"""
if current_user is None:
raise HTTPException(status_code=401, detail="Authentication required")
task = download_manager.get_task(task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")