""" Главный модуль приложения Telegram Gateway. Автор: Сергей Антропов Сайт: https://devops.org.ru """ import logging from fastapi import FastAPI from app.common.cors import add as add_cors from app.common.telemetry import add as add_telemetry from app.api.v1.router import api_router # Настройка логирования (базовая настройка, детальная настройка в app.common.logger) logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # Создаем приложение FastAPI app = FastAPI( title="Message Gateway", summary="Отправляем мониторинговые алерты в телеграм/MAX и создаем тикеты в Jira", description=( "Приложение для оповещений, приходящих из Grafana/Zabbix/AlertManager " "посредством вебхука, в телеграм/MAX. С возможностью отправок в треды и создания тикетов в Jira. " "

Что бы начать отправлять сообщения, добавьте бота " "@CismGlobalMonitoring_bot в чат и внесите изменения в группы" ), version="0.2.0", contact={ "name": "Сергей Антропов", "url": "https://devops.org.ru/contact/", "email": "sergey@antropoff.ru", }, license_info={ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html", }, debug=False, swagger_ui_init_oauth={ "clientId": "api-key", "appName": "Message Gateway API", "usePkceWithAuthorizationCodeGrant": False, } ) # Добавляем схему безопасности в Swagger from fastapi.openapi.utils import get_openapi from fastapi.routing import APIRoute def custom_openapi(): if app.openapi_schema: return app.openapi_schema # Собираем пути, которые должны быть скрыты от API hidden_paths = set() # Перебираем все routes и находим те, которые помечены как скрытые for route in app.routes: # Проверяем только APIRoute if not isinstance(route, APIRoute): continue # Получаем endpoint функцию endpoint = route.endpoint # Проверяем, помечен ли endpoint как скрытый от API if hasattr(endpoint, "__hide_from_api__") and endpoint.__hide_from_api__: # Получаем полный путь (с учетом префиксов) path = route.path # Нормализуем путь (убираем параметры типа {param} для сопоставления) # Но нам нужно точное сопоставление, поэтому используем полный путь hidden_paths.add(path) logger.debug(f"Эндпоинт {path} помечен как скрытый от API") # Генерируем схему как обычно openapi_schema = get_openapi( title=app.title, version=app.version, description=app.description, routes=app.routes, ) # Удаляем скрытые пути из схемы if hidden_paths: paths = openapi_schema.get("paths", {}) # Создаем новый словарь paths без скрытых путей filtered_paths = {} for path, path_item in paths.items(): # Проверяем, должен ли путь быть скрыт # Путь в схеме должен точно совпадать с путем в route.path # route.path уже содержит все префиксы (router prefix + route path) should_hide = path in hidden_paths if not should_hide: filtered_paths[path] = path_item else: logger.debug(f"Удаляем путь {path} из OpenAPI схемы") openapi_schema["paths"] = filtered_paths # Добавляем схему безопасности API Key if "components" not in openapi_schema: openapi_schema["components"] = {} if "securitySchemes" not in openapi_schema["components"]: openapi_schema["components"]["securitySchemes"] = {} # Определяем имя схемы безопасности, которое использует FastAPI # FastAPI автоматически генерирует имя на основе класса Security # Обычно это имя класса в camelCase (например, "APIKeyHeader") api_key_scheme_name = None # Перебираем существующие схемы безопасности и находим API Key схему for scheme_name, scheme_def in openapi_schema["components"].get("securitySchemes", {}).items(): if scheme_def.get("type") == "apiKey" and scheme_def.get("name") == "X-API-Key": api_key_scheme_name = scheme_name break # Если схема не найдена, создаем новую с именем "ApiKeyAuth" if not api_key_scheme_name: api_key_scheme_name = "ApiKeyAuth" openapi_schema["components"]["securitySchemes"][api_key_scheme_name] = { "type": "apiKey", "in": "header", "name": "X-API-Key", "description": "API ключ для авторизации. Получите его из переменной окружения API_KEY." } else: # Обновляем существующую схему, если она уже есть openapi_schema["components"]["securitySchemes"][api_key_scheme_name] = { "type": "apiKey", "in": "header", "name": "X-API-Key", "description": "API ключ для авторизации. Получите его из переменной окружения API_KEY." } # Если имя схемы не "ApiKeyAuth", переименовываем ее if api_key_scheme_name != "ApiKeyAuth": # Сохраняем старую схему old_scheme = openapi_schema["components"]["securitySchemes"].pop(api_key_scheme_name) # Создаем новую схему с именем "ApiKeyAuth" openapi_schema["components"]["securitySchemes"]["ApiKeyAuth"] = old_scheme # Заменяем все использования старого имени на новое в security эндпоинтов for path, path_item in openapi_schema.get("paths", {}).items(): for method, operation in path_item.items(): if isinstance(operation, dict) and "security" in operation: security_list = operation["security"] for security_item in security_list: if api_key_scheme_name in security_item: # Заменяем старое имя на новое security_item["ApiKeyAuth"] = security_item.pop(api_key_scheme_name) # Убеждаемся, что схема "ApiKeyAuth" существует if "ApiKeyAuth" not in openapi_schema["components"]["securitySchemes"]: openapi_schema["components"]["securitySchemes"]["ApiKeyAuth"] = { "type": "apiKey", "in": "header", "name": "X-API-Key", "description": "API ключ для авторизации. Получите его из переменной окружения API_KEY." } app.openapi_schema = openapi_schema return app.openapi_schema app.openapi = custom_openapi # Добавляем CORS add_cors(app) # Добавляем телеметрию add_telemetry(app) # Подключаем роутер API v1 app.include_router(api_router) if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host="0.0.0.0", port=8000, reload=False )