Initial commit: Message Gateway project
- FastAPI приложение для отправки мониторинговых алертов в мессенджеры - Поддержка Telegram и MAX/VK - Интеграция с Grafana, Zabbix, AlertManager - Автоматическое создание тикетов в Jira - Управление группами мессенджеров через API - Декораторы для авторизации и скрытия эндпоинтов - Подробная документация в папке docs/ Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
511
app/api/v1/endpoints/groups.py
Normal file
511
app/api/v1/endpoints/groups.py
Normal file
@@ -0,0 +1,511 @@
|
||||
"""
|
||||
Эндпоинты для управления группами мессенджеров.
|
||||
|
||||
Автор: Сергей Антропов
|
||||
Сайт: 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)}")
|
||||
Reference in New Issue
Block a user