133 lines
4.9 KiB
Python
133 lines
4.9 KiB
Python
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
from api.v1.schemas.settings import (
|
|
LastFmAuthTokenResponse,
|
|
LastFmAuthSessionRequest,
|
|
LastFmAuthSessionResponse,
|
|
LastFmConnectionSettings,
|
|
LASTFM_SECRET_MASK,
|
|
)
|
|
from core.dependencies import (
|
|
get_lastfm_auth_service,
|
|
get_lastfm_repository,
|
|
get_preferences_service,
|
|
clear_lastfm_dependent_caches,
|
|
)
|
|
from core.exceptions import ConfigurationError, ExternalServiceError, TokenNotAuthorizedError
|
|
from infrastructure.msgspec_fastapi import MsgSpecBody, MsgSpecRoute
|
|
from services.lastfm_auth_service import LastFmAuthService
|
|
from services.preferences_service import PreferencesService
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(route_class=MsgSpecRoute, prefix="/lastfm", tags=["lastfm"])
|
|
|
|
|
|
@router.post("/auth/token", response_model=LastFmAuthTokenResponse)
|
|
async def request_auth_token(
|
|
auth_service: LastFmAuthService = Depends(get_lastfm_auth_service),
|
|
preferences_service: PreferencesService = Depends(get_preferences_service),
|
|
):
|
|
try:
|
|
settings = preferences_service.get_lastfm_connection()
|
|
if not settings.api_key or not settings.shared_secret:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Add a Last.fm API key and shared secret first",
|
|
)
|
|
|
|
token, auth_url = await auth_service.request_token(settings.api_key)
|
|
logger.info(
|
|
"Last.fm auth token requested",
|
|
extra={"step": "token_requested", "status": "success"},
|
|
)
|
|
return LastFmAuthTokenResponse(token=token, auth_url=auth_url)
|
|
except HTTPException:
|
|
raise
|
|
except ConfigurationError as e:
|
|
logger.warning(
|
|
"Last.fm auth token request failed (config): %s",
|
|
e,
|
|
extra={"step": "token_requested", "status": "config_error"},
|
|
)
|
|
raise HTTPException(status_code=400, detail="Last.fm settings are incomplete or invalid")
|
|
except ExternalServiceError as e:
|
|
logger.warning(
|
|
"Last.fm auth token request failed (external): %s",
|
|
e,
|
|
extra={"step": "token_requested", "status": "external_error"},
|
|
)
|
|
raise HTTPException(status_code=502, detail="Couldn't reach Last.fm for a sign-in token")
|
|
|
|
|
|
@router.post("/auth/session", response_model=LastFmAuthSessionResponse)
|
|
async def exchange_auth_session(
|
|
request: LastFmAuthSessionRequest = MsgSpecBody(LastFmAuthSessionRequest),
|
|
auth_service: LastFmAuthService = Depends(get_lastfm_auth_service),
|
|
preferences_service: PreferencesService = Depends(get_preferences_service),
|
|
):
|
|
try:
|
|
username, session_key, _ = await auth_service.exchange_session(request.token)
|
|
|
|
settings = preferences_service.get_lastfm_connection()
|
|
updated = LastFmConnectionSettings(
|
|
api_key=settings.api_key,
|
|
shared_secret=settings.shared_secret,
|
|
session_key=session_key,
|
|
username=username,
|
|
enabled=settings.enabled,
|
|
)
|
|
preferences_service.save_lastfm_connection(updated)
|
|
get_lastfm_repository.cache_clear()
|
|
get_lastfm_auth_service.cache_clear()
|
|
clear_lastfm_dependent_caches()
|
|
logger.info(
|
|
"Last.fm session exchanged for user %s",
|
|
username,
|
|
extra={"step": "session_exchanged", "status": "success"},
|
|
)
|
|
|
|
return LastFmAuthSessionResponse(
|
|
username=username,
|
|
success=True,
|
|
message=f"Connected as {username}",
|
|
)
|
|
except TokenNotAuthorizedError:
|
|
message = "Last.fm access hasn't been approved yet. Authorize it in the Last.fm tab, then try again."
|
|
error_code = "token_not_authorized"
|
|
logger.warning(
|
|
"Last.fm session exchange failed: token not authorized",
|
|
extra={
|
|
"step": "session_exchanged",
|
|
"status": "token_not_authorized",
|
|
"error_code": error_code,
|
|
},
|
|
)
|
|
raise HTTPException(status_code=502, detail=message)
|
|
except ExternalServiceError as e:
|
|
message = "Couldn't finish the Last.fm sign-in. Please try again."
|
|
error_code = "external_error"
|
|
logger.warning(
|
|
"Last.fm session exchange failed: %s",
|
|
e,
|
|
extra={
|
|
"step": "session_exchanged",
|
|
"status": "external_error",
|
|
"error_code": error_code,
|
|
},
|
|
)
|
|
raise HTTPException(status_code=502, detail=message)
|
|
except ConfigurationError as e:
|
|
logger.warning(
|
|
"Last.fm session exchange rejected: %s",
|
|
e,
|
|
extra={
|
|
"step": "session_exchanged",
|
|
"status": "configuration_error",
|
|
"error_code": "configuration_error",
|
|
},
|
|
)
|
|
raise HTTPException(status_code=422, detail="Last.fm configuration error. Check your settings and try again.")
|