from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from typing import Annotated, Dict, List import httpx from loguru import logger from app.core.config import settings from app.api.v1.schemas.counter import CounterListResponse, Counter, CounterCreateRequest, CounterCreateResponse from app.api.v1.schemas.goal import GoalCreateRequest, GoalCreateResponse, Goal router = APIRouter() bearer_scheme = HTTPBearer() # ФИНАЛЬНАЯ ВЕРСИЯ: Ключи используют правильное написание "Marquiz" (в нижнем регистре для сравнения). PREDEFINED_GOALS: Dict[str, str] = { "marquiz-start": "Посетитель открыл квиз", "marquiz-startquiz": "Посетитель нажал на кнопку стартовой страницы", "marquiz-form": "Посетитель дошёл до формы контактов", "marquiz-result": "Посетитель увидел результат", "marquiz-contacts1": "Посетитель заполнил и отправил форму контактов", } # --- КОД ЭНДПОИНТОВ get_counters и create_counter ОСТАЕТСЯ БЕЗ ИЗМЕНЕНИЙ --- @router.get("/", response_model=CounterListResponse, summary="Получение списка счетчиков") async def get_counters(credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)]): token = credentials.credentials logger.info("Fetching list of counters from Yandex.Metrika API.") url = f"{settings.YANDEX_METRIKA_API_URL}/management/v1/counters" headers = {'Authorization': f'OAuth {token}', 'Content-Type': 'application/json'} try: async with httpx.AsyncClient() as client: response = await client.get(url, headers=headers) response.raise_for_status() data = response.json() counters_list = [ Counter(id=c['id'], name=c['name'], site=c['site']) for c in data.get('counters', []) ] logger.success(f"Successfully fetched {len(counters_list)} counters.") return CounterListResponse(counters=counters_list) except httpx.HTTPStatusError as e: error_details = e.response.json() logger.error(f"Yandex Metrika API error: {e.response.status_code} - {error_details}") raise HTTPException( status_code=e.response.status_code, detail=f"Yandex API Error: {error_details.get('message', 'Unknown error')}" ) except Exception as e: logger.opt(exception=True).error("An unexpected error occurred while fetching counters.") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An internal server error occurred." ) @router.post( "/", response_model=CounterCreateResponse, status_code=status.HTTP_201_CREATED, summary="Создание нового счетчика" ) async def create_counter( request_body: CounterCreateRequest, credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)] ): token = credentials.credentials site_url = str(request_body.site_url).strip('/') site_name = site_url.replace("https://", "").replace("http://", "") logger.info(f"Attempting to create a new counter for site: {site_name}") url = f"{settings.YANDEX_METRIKA_API_URL}/management/v1/counters" headers = {'Authorization': f'OAuth {token}', 'Content-Type': 'application/json'} payload = { "counter": { "name": site_name, "site": site_name } } try: async with httpx.AsyncClient() as client: response = await client.post(url, headers=headers, json=payload) response.raise_for_status() created_counter_data = response.json().get("counter", {}) logger.success(f"Successfully created counter ID: {created_counter_data.get('id')}") return CounterCreateResponse( id=created_counter_data.get('id'), name=created_counter_data.get('name'), code_status=created_counter_data.get('code_status') ) except httpx.HTTPStatusError as e: error_details = e.response.json() logger.error(f"Yandex Metrika API error during counter creation: {e.response.status_code} - {error_details}") raise HTTPException( status_code=e.response.status_code, detail=f"Yandex API Error: {error_details.get('message', 'Unknown error')}" ) except Exception as e: logger.opt(exception=True).error("An unexpected error occurred during counter creation.") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="An internal server error occurred." ) @router.post( "/{counter_id}/goals", response_model=GoalCreateResponse, status_code=status.HTTP_201_CREATED, summary="Создание стандартного набора целей в счетчике" ) async def create_goals_for_counter( counter_id: int, request_body: GoalCreateRequest, credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)] ): token = credentials.credentials logger.info(f"Attempting to create goals for counter ID: {counter_id}") url = f"{settings.YANDEX_METRIKA_API_URL}/management/v1/counter/{counter_id}/goals" headers = {'Authorization': f'OAuth {token}', 'Content-Type': 'application/json'} created_goals_list: List[Goal] = [] async with httpx.AsyncClient() as client: for identifier in request_body.goal_identifiers: # Приводим входящий идентификатор к нижнему регистру для надежного поиска goal_name = PREDEFINED_GOALS.get(identifier.lower()) if not goal_name: logger.warning(f"Unknown goal identifier '{identifier}' skipped.") continue goal_payload = { "name": goal_name, "type": "action", "conditions": [{"type": "exact", "url": identifier}] } payload = {"goal": goal_payload} logger.debug(f"Sending payload for goal '{goal_name}': {payload}") try: response = await client.post(url, headers=headers, json=payload) response.raise_for_status() created_goal_data = response.json().get("goal", {}) if created_goal_data: logger.success(f"Successfully created goal: {created_goal_data.get('name')} (ID: {created_goal_data.get('id')})") created_goals_list.append( Goal(id=created_goal_data['id'], name=created_goal_data['name']) ) except httpx.HTTPStatusError as e: error_details = e.response.json() logger.error(f"Failed to create goal '{goal_name}'. Reason: {error_details.get('message', 'Unknown error')}") except Exception: logger.opt(exception=True).error(f"An unexpected error occurred while creating goal '{goal_name}'.") if not created_goals_list: logger.warning("No goals were created.") raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Could not create any of the requested goals. Check logs for details." ) return GoalCreateResponse(created_goals=created_goals_list)