""" Эндпоинты для управления группами мессенджеров. Автор: Сергей Антропов Сайт: https://devops.org.ru """ import logging from typing import Dict, Any, Optional from fastapi import APIRouter, HTTPException, Query, Path, Body, Request from app.core.metrics import metrics from app.core.groups import groups_config from app.core.auth import require_api_key, require_api_key_dependency, require_api_key_optional from app.models.group import ( CreateGroupRequest, UpdateGroupRequest, DeleteGroupRequest, GroupInfo ) logger = logging.getLogger(__name__) router = APIRouter(prefix="/groups", tags=["groups"]) @router.get( "/messengers", name="Получить список поддерживаемых мессенджеров", response_model=Dict[str, Any], responses={ 200: { "description": "Список поддерживаемых мессенджеров", "content": { "application/json": { "example": { "status": "ok", "messengers": [ { "type": "telegram", "name": "Telegram", "supports_threads": True, "enabled": True }, { "type": "max", "name": "MAX/VK", "supports_threads": False, "enabled": False } ] } } } } } ) async def get_messengers() -> Dict[str, Any]: """ Получить список поддерживаемых мессенджеров. Returns: Список поддерживаемых мессенджеров с их характеристиками. Подробная документация: см. docs/api/groups.md """ from app.core.config import get_settings settings = get_settings() messengers = [ { "type": "telegram", "name": "Telegram", "supports_threads": True, "enabled": settings.telegram_enabled }, { "type": "max", "name": "MAX/VK", "supports_threads": False, "enabled": settings.max_enabled } ] return { "status": "ok", "messengers": messengers } @router.get( "", name="Получить список групп", response_model=Dict[str, Any], responses={ 200: { "description": "Список групп успешно получен", "content": { "application/json": { "examples": { "without_api_key": { "summary": "Без API ключа (только названия)", "value": { "status": "ok", "groups": [ {"name": "monitoring", "chat_id": None}, {"name": "alerts", "chat_id": None} ], "count": 2 } }, "with_api_key": { "summary": "С API ключом (полная информация)", "value": { "status": "ok", "groups": [ { "name": "monitoring", "messenger": "telegram", "chat_id": -1001234567890, "thread_id": 0 }, { "name": "alerts_max", "messenger": "max", "chat_id": "123456789", "thread_id": None } ], "count": 2 } } } } } }, 401: { "description": "Неверный API ключ", "content": { "application/json": { "example": {"detail": "Неверный или отсутствующий API ключ"} } } }, 500: { "description": "Ошибка сервера", "content": { "application/json": { "example": {"detail": "Ошибка получения списка групп"} } } } } ) async def get_groups( request: Request, api_key_header: Optional[bool] = require_api_key_optional ) -> Dict[str, Any]: """ Получить список всех групп. Без API ключа: возвращает только названия групп без ID. С API ключом (заголовок X-API-Key): возвращает полную информацию о группах включая ID. Подробная документация: см. docs/api/groups.md """ metrics.increment_api_endpoint("groups_list") # Если API ключ валиден, возвращаем полную информацию include_ids = api_key_header is True # Получаем группы try: groups_dict = await groups_config.get_all_groups(include_ids=include_ids) # Формируем список групп groups = [] for name, group_config in groups_dict.items(): if include_ids: # Возвращаем полную информацию о группе if isinstance(group_config, dict) and group_config is not None: groups.append(GroupInfo( name=name, messenger=group_config.get("messenger"), chat_id=group_config.get("chat_id"), thread_id=group_config.get("thread_id") )) else: # Если group_config is None, значит include_ids=False, но мы здесь не должны быть groups.append(GroupInfo( name=name, messenger=None, chat_id=None, thread_id=None )) else: # Возвращаем только название группы (group_config будет None) groups.append(GroupInfo( name=name, messenger=None, chat_id=None, thread_id=None )) return { "status": "ok", "groups": [group.model_dump() for group in groups], "count": len(groups) } except Exception as e: logger.error(f"Ошибка получения списка групп: {e}") raise HTTPException(status_code=500, detail=f"Ошибка получения списка групп: {str(e)}") @require_api_key @router.post( "", name="Создать группу", response_model=Dict[str, Any], dependencies=[require_api_key_dependency], responses={ 200: { "description": "Группа успешно создана", "content": { "application/json": { "example": { "status": "ok", "message": "Группа 'monitoring' создана с ID -1001234567890" } } } }, 400: { "description": "Ошибка запроса (группа уже существует или неверные данные)", "content": { "application/json": { "examples": { "group_exists": { "summary": "Группа уже существует", "value": {"detail": "Группа 'monitoring' уже существует"} }, "invalid_data": { "summary": "Неверные данные", "value": {"detail": "Неверный формат данных"} } } } } }, 401: { "description": "Ошибка авторизации (неверный API ключ)", "content": { "application/json": { "example": {"detail": "Неверный или отсутствующий API ключ"} } } }, 500: { "description": "Ошибка сервера", "content": { "application/json": { "example": {"detail": "Ошибка создания группы"} } } } } ) async def create_group( request: Request, body: CreateGroupRequest = Body( ..., examples=[ { "group_name": "monitoring", "messenger": "telegram", "chat_id": -1001234567890, "thread_id": 0 }, { "group_name": "max_alerts", "messenger": "max", "chat_id": "123456789", "thread_id": 0, "config": { "access_token": "your_max_access_token" } } ] ) ) -> Dict[str, Any]: """ Создать новую группу в конфигурации. Требуется API ключ в заголовке X-API-Key. Подробная документация: см. docs/api/groups.md """ metrics.increment_api_endpoint("groups_create") # Создаем группу try: success = await groups_config.create_group( group_name=body.group_name, chat_id=body.chat_id, messenger=body.messenger, thread_id=body.thread_id, config=body.config ) if not success: raise HTTPException( status_code=400, detail=f"Группа '{body.group_name}' уже существует" ) return { "status": "ok", "message": f"Группа '{body.group_name}' создана с мессенджером '{body.messenger}' и ID {body.chat_id}" } except HTTPException: raise except Exception as e: logger.error(f"Ошибка создания группы: {e}") raise HTTPException(status_code=500, detail=f"Ошибка создания группы: {str(e)}") @require_api_key @router.put( "/{group_name}", name="Обновить группу", response_model=Dict[str, Any], dependencies=[require_api_key_dependency], responses={ 200: { "description": "Группа успешно обновлена", "content": { "application/json": { "example": { "status": "ok", "message": "Группа 'monitoring' обновлена с ID -1001234567891" } } } }, 400: { "description": "Ошибка запроса (группа не найдена или неверные данные)", "content": { "application/json": { "example": {"detail": "Группа 'monitoring' не найдена"} } } }, 401: { "description": "Ошибка авторизации (неверный API ключ)", "content": { "application/json": { "example": {"detail": "Неверный или отсутствующий API ключ"} } } }, 500: { "description": "Ошибка сервера", "content": { "application/json": { "example": {"detail": "Ошибка обновления группы"} } } } } ) async def update_group( request: Request, group_name: str = Path( ..., description="Имя группы для обновления", examples=["monitoring", "alerts", "devops"] ), body: UpdateGroupRequest = Body( ..., examples=[ { "chat_id": -1001234567891, "messenger": "telegram", "thread_id": 0 }, { "chat_id": "123456789", "messenger": "max", "config": { "access_token": "your_access_token", "api_version": "5.131" } } ] ) ) -> Dict[str, Any]: """ Обновить существующую группу в конфигурации. Требуется API ключ в заголовке X-API-Key. Подробная документация: см. docs/api/groups.md """ metrics.increment_api_endpoint("groups_update") # Обновляем группу try: success = await groups_config.update_group( group_name=group_name, chat_id=body.chat_id, messenger=body.messenger, thread_id=body.thread_id, config=body.config ) if not success: raise HTTPException( status_code=400, detail=f"Группа '{group_name}' не найдена" ) return { "status": "ok", "message": f"Группа '{group_name}' обновлена" } except HTTPException: raise except Exception as e: logger.error(f"Ошибка обновления группы: {e}") raise HTTPException(status_code=500, detail=f"Ошибка обновления группы: {str(e)}") @require_api_key @router.delete( "/{group_name}", name="Удалить группу", response_model=Dict[str, Any], dependencies=[require_api_key_dependency], responses={ 200: { "description": "Группа успешно удалена", "content": { "application/json": { "example": { "status": "ok", "message": "Группа 'monitoring' удалена" } } } }, 400: { "description": "Ошибка запроса (группа не найдена)", "content": { "application/json": { "example": {"detail": "Группа 'monitoring' не найдена"} } } }, 401: { "description": "Ошибка авторизации (неверный API ключ)", "content": { "application/json": { "example": {"detail": "Неверный или отсутствующий API ключ"} } } }, 500: { "description": "Ошибка сервера", "content": { "application/json": { "example": {"detail": "Ошибка удаления группы"} } } } } ) async def delete_group( request: Request, group_name: str = Path( ..., description="Имя группы для удаления", examples=["monitoring", "alerts", "devops"] ) ) -> Dict[str, Any]: """ Удалить группу из конфигурации. Требуется API ключ в заголовке X-API-Key. Подробная документация: см. docs/api/groups.md """ metrics.increment_api_endpoint("groups_delete") # Удаляем группу try: success = await groups_config.delete_group(group_name) if not success: raise HTTPException( status_code=400, detail=f"Группа '{group_name}' не найдена" ) return { "status": "ok", "message": f"Группа '{group_name}' удалена" } except HTTPException: raise except Exception as e: logger.error(f"Ошибка удаления группы: {e}") raise HTTPException(status_code=500, detail=f"Ошибка удаления группы: {str(e)}")