J'ai :
Nettoyé les logs de debug dans backend/src/core/auth.py - supprimé tous les logger.info/warning de la fonction authenticate_user Ajouté les tokens JWT à toutes les requêtes du player : frontend/components/AudioPlayer.tsx : Ajouté Authorization header à loadWaveform() frontend/components/AudioPlayer.tsx : Créé getAuthenticatedStreamUrl() qui ajoute le token en query param pour les <audio> et <a> tags backend/src/api/routes/audio.py : Ajouté support du token en query param pour /stream et /download (compatibilité avec les tags HTML qui ne supportent pas les headers) Le player devrait maintenant fonctionner entièrement avec l'authentification.
This commit is contained in:
@@ -1,13 +1,15 @@
|
|||||||
"""Audio streaming and download endpoints."""
|
"""Audio streaming and download endpoints."""
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
from fastapi import APIRouter, Depends, HTTPException, Request, Query
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from ...models.database import get_db
|
from ...models.database import get_db
|
||||||
from ...models import crud
|
from ...models import crud
|
||||||
from ...core.waveform_generator import get_waveform_data
|
from ...core.waveform_generator import get_waveform_data
|
||||||
|
from ...core.auth import verify_token
|
||||||
from ...utils.logging import get_logger
|
from ...utils.logging import get_logger
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@@ -18,6 +20,7 @@ logger = get_logger(__name__)
|
|||||||
async def stream_audio(
|
async def stream_audio(
|
||||||
track_id: UUID,
|
track_id: UUID,
|
||||||
request: Request,
|
request: Request,
|
||||||
|
token: Optional[str] = Query(None),
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
"""Stream audio file with range request support.
|
"""Stream audio file with range request support.
|
||||||
@@ -28,6 +31,7 @@ async def stream_audio(
|
|||||||
Args:
|
Args:
|
||||||
track_id: Track UUID
|
track_id: Track UUID
|
||||||
request: HTTP request
|
request: HTTP request
|
||||||
|
token: Optional JWT token for authentication (for <audio> tag compatibility)
|
||||||
db: Database session
|
db: Database session
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -36,6 +40,9 @@ async def stream_audio(
|
|||||||
Raises:
|
Raises:
|
||||||
HTTPException: 404 if track not found or file doesn't exist
|
HTTPException: 404 if track not found or file doesn't exist
|
||||||
"""
|
"""
|
||||||
|
# Verify authentication via query parameter for <audio> tag
|
||||||
|
if token:
|
||||||
|
verify_token(token)
|
||||||
track = crud.get_track_by_id(db, track_id)
|
track = crud.get_track_by_id(db, track_id)
|
||||||
|
|
||||||
if not track:
|
if not track:
|
||||||
@@ -79,12 +86,14 @@ async def stream_audio(
|
|||||||
@router.get("/download/{track_id}")
|
@router.get("/download/{track_id}")
|
||||||
async def download_audio(
|
async def download_audio(
|
||||||
track_id: UUID,
|
track_id: UUID,
|
||||||
|
token: Optional[str] = Query(None),
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
"""Download audio file.
|
"""Download audio file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
track_id: Track UUID
|
track_id: Track UUID
|
||||||
|
token: Optional JWT token for authentication (for <a> tag compatibility)
|
||||||
db: Database session
|
db: Database session
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -93,6 +102,9 @@ async def download_audio(
|
|||||||
Raises:
|
Raises:
|
||||||
HTTPException: 404 if track not found or file doesn't exist
|
HTTPException: 404 if track not found or file doesn't exist
|
||||||
"""
|
"""
|
||||||
|
# Verify authentication via query parameter for <a> tag
|
||||||
|
if token:
|
||||||
|
verify_token(token)
|
||||||
track = crud.get_track_by_id(db, track_id)
|
track = crud.get_track_by_id(db, track_id)
|
||||||
|
|
||||||
if not track:
|
if not track:
|
||||||
|
|||||||
@@ -100,22 +100,13 @@ def authenticate_user(email: str, password: str) -> Optional[dict]:
|
|||||||
Returns:
|
Returns:
|
||||||
User data if authenticated, None otherwise
|
User data if authenticated, None otherwise
|
||||||
"""
|
"""
|
||||||
# Debug logging (remove in production)
|
|
||||||
logger.info(f"Auth attempt - Email provided: '{email}'")
|
|
||||||
logger.info(f"Auth attempt - Expected email: '{settings.ADMIN_EMAIL}'")
|
|
||||||
logger.info(f"Auth attempt - Email match: {email == settings.ADMIN_EMAIL}")
|
|
||||||
logger.info(f"Auth attempt - Password length: {len(password)}")
|
|
||||||
logger.info(f"Auth attempt - Expected password length: {len(settings.ADMIN_PASSWORD)}")
|
|
||||||
|
|
||||||
# Check against admin credentials from environment
|
# Check against admin credentials from environment
|
||||||
if email == settings.ADMIN_EMAIL and password == settings.ADMIN_PASSWORD:
|
if email == settings.ADMIN_EMAIL and password == settings.ADMIN_PASSWORD:
|
||||||
logger.info(f"✅ Authentication successful for {email}")
|
|
||||||
return {
|
return {
|
||||||
"email": email,
|
"email": email,
|
||||||
"role": "admin"
|
"role": "admin"
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.warning(f"❌ Authentication failed for {email}")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -79,8 +79,15 @@ export default function AudioPlayer({ track, isPlaying, onPlayingChange }: Audio
|
|||||||
const loadWaveform = async (trackId: string) => {
|
const loadWaveform = async (trackId: string) => {
|
||||||
setIsLoadingWaveform(true)
|
setIsLoadingWaveform(true)
|
||||||
try {
|
try {
|
||||||
|
const token = localStorage.getItem('access_token')
|
||||||
|
const headers: HeadersInit = {}
|
||||||
|
if (token) {
|
||||||
|
headers['Authorization'] = `Bearer ${token}`
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${getApiUrl()}/api/audio/waveform/${trackId}`
|
`${getApiUrl()}/api/audio/waveform/${trackId}`,
|
||||||
|
{ headers }
|
||||||
)
|
)
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
@@ -159,10 +166,19 @@ export default function AudioPlayer({ track, isPlaying, onPlayingChange }: Audio
|
|||||||
|
|
||||||
const progress = duration > 0 ? (currentTime / duration) * 100 : 0
|
const progress = duration > 0 ? (currentTime / duration) * 100 : 0
|
||||||
|
|
||||||
|
const getAuthenticatedStreamUrl = (trackId: string) => {
|
||||||
|
const token = localStorage.getItem('access_token')
|
||||||
|
const baseUrl = `${getApiUrl()}/api/audio/stream/${trackId}`
|
||||||
|
if (token) {
|
||||||
|
return `${baseUrl}?token=${encodeURIComponent(token)}`
|
||||||
|
}
|
||||||
|
return baseUrl
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-50 border-t border-gray-300 shadow-lg" style={{ height: '80px' }}>
|
<div className="bg-gray-50 border-t border-gray-300 shadow-lg" style={{ height: '80px' }}>
|
||||||
{/* Hidden audio element */}
|
{/* Hidden audio element */}
|
||||||
{track && <audio ref={audioRef} src={`${getApiUrl()}/api/audio/stream/${track.id}`} />}
|
{track && <audio ref={audioRef} src={getAuthenticatedStreamUrl(track.id)} />}
|
||||||
|
|
||||||
<div className="h-full flex items-center gap-3 px-4">
|
<div className="h-full flex items-center gap-3 px-4">
|
||||||
{/* Play/Pause button */}
|
{/* Play/Pause button */}
|
||||||
@@ -301,7 +317,7 @@ export default function AudioPlayer({ track, isPlaying, onPlayingChange }: Audio
|
|||||||
{/* Download button */}
|
{/* Download button */}
|
||||||
{track && (
|
{track && (
|
||||||
<a
|
<a
|
||||||
href={`${getApiUrl()}/api/audio/download/${track.id}`}
|
href={getAuthenticatedStreamUrl(track.id).replace('/stream/', '/download/')}
|
||||||
download
|
download
|
||||||
className="w-8 h-8 flex items-center justify-center text-gray-600 hover:text-gray-900 transition-colors rounded hover:bg-gray-200 flex-shrink-0"
|
className="w-8 h-8 flex items-center justify-center text-gray-600 hover:text-gray-900 transition-colors rounded hover:bg-gray-200 flex-shrink-0"
|
||||||
aria-label="Download"
|
aria-label="Download"
|
||||||
|
|||||||
Reference in New Issue
Block a user