93 lines
4.0 KiB
Python
93 lines
4.0 KiB
Python
from fastapi import APIRouter, HTTPException, status, Depends
|
||
from fastapi.responses import JSONResponse
|
||
import httpx
|
||
from loguru import logger
|
||
from typing import Annotated
|
||
|
||
from app.core.config import settings
|
||
# ИСПРАВЛЕНИЕ ЗДЕСЬ
|
||
from app.api.v1.schemas.auth import TokenRequest, TokenResponse, UserInfoResponse
|
||
|
||
# --- ДОБАВЛЕНО ДЛЯ НОВОГО ЭНДПОИНТА ---
|
||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||
|
||
router = APIRouter()
|
||
bearer_scheme = HTTPBearer()
|
||
|
||
YANDEX_TOKEN_URL = "https://oauth.yandex.ru/token"
|
||
# --- ДОБАВЛЕНО ДЛЯ НОВОГО ЭНДПОИНТА ---
|
||
YANDEX_USERINFO_URL = "https://login.yandex.ru/info"
|
||
|
||
@router.post("/token", response_model=TokenResponse, summary="Обмен кода авторизации на токен доступа")
|
||
async def exchange_code_for_token(request: TokenRequest):
|
||
"""
|
||
Принимает временный 'code' от фронтенда, обменивает его на 'access_token'
|
||
у Яндекса и возвращает токен клиенту.
|
||
"""
|
||
logger.info("Attempting to exchange authorization code for an access token.")
|
||
|
||
payload = {
|
||
'grant_type': 'authorization_code',
|
||
'code': request.code,
|
||
'client_id': settings.YANDEX_CLIENT_ID,
|
||
'client_secret': settings.YANDEX_CLIENT_SECRET
|
||
}
|
||
|
||
try:
|
||
async with httpx.AsyncClient() as client:
|
||
response = await client.post(YANDEX_TOKEN_URL, data=payload)
|
||
response.raise_for_status()
|
||
|
||
token_data = response.json()
|
||
logger.success("Successfully received access token from Yandex.")
|
||
|
||
return TokenResponse(
|
||
access_token=token_data.get("access_token"),
|
||
token_type=token_data.get("token_type", "bearer")
|
||
)
|
||
|
||
except httpx.HTTPStatusError as e:
|
||
error_details = e.response.json()
|
||
logger.error(f"Yandex OAuth error: {e.response.status_code} - {error_details}")
|
||
raise HTTPException(
|
||
status_code=e.response.status_code,
|
||
detail=f"Yandex OAuth error: {error_details.get('error_description', 'Unknown error')}"
|
||
)
|
||
except Exception as e:
|
||
logger.opt(exception=True).error("An unexpected error occurred during token exchange.")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail="An internal server error occurred."
|
||
)
|
||
|
||
# --- НОВЫЙ ЭНДПОИНТ ---
|
||
@router.get("/userinfo", response_model=UserInfoResponse, summary="Получение информации о пользователе")
|
||
async def get_user_info(credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)]):
|
||
"""
|
||
Используя access_token, получает информацию о пользователе Яндекса.
|
||
Токен должен быть передан в заголовке 'Authorization: Bearer <token>'.
|
||
"""
|
||
token = credentials.credentials
|
||
headers = {'Authorization': f'OAuth {token}'}
|
||
|
||
try:
|
||
async with httpx.AsyncClient() as client:
|
||
response = await client.get(YANDEX_USERINFO_URL, headers=headers)
|
||
response.raise_for_status()
|
||
user_data = response.json()
|
||
logger.info(f"Fetched user info for login: {user_data.get('login')}")
|
||
return UserInfoResponse(**user_data)
|
||
|
||
except httpx.HTTPStatusError as e:
|
||
error_details = e.response.json()
|
||
logger.error(f"Yandex UserInfo API error: {e.response.status_code} - {error_details}")
|
||
raise HTTPException(
|
||
status_code=e.response.status_code,
|
||
detail=f"Yandex API Error: {error_details.get('error_description', 'Unknown error')}"
|
||
)
|
||
except Exception:
|
||
logger.opt(exception=True).error("An unexpected error occurred while fetching user info.")
|
||
raise HTTPException(
|
||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||
detail="An internal server error occurred."
|
||
) |