import asyncio import os from importlib import import_module from os.path import exists, join from ariadne import load_schema_from_path, make_executable_schema from ariadne.asgi import GraphQL from ariadne.asgi.handlers import GraphQLHTTPHandler from starlette.applications import Starlette from starlette.middleware.cors import CORSMiddleware from starlette.middleware.authentication import AuthenticationMiddleware from starlette.middleware import Middleware from starlette.requests import Request from starlette.responses import FileResponse, JSONResponse, HTMLResponse, RedirectResponse from starlette.routing import Route, Mount from starlette.staticfiles import StaticFiles from cache.precache import precache_data from cache.revalidator import revalidation_manager from services.exception import ExceptionHandlerMiddleware from services.redis import redis from services.schema import create_all_tables, resolvers from services.search import search_service from settings import DEV_SERVER_PID_FILE_NAME, MODE, ADMIN_EMAILS from utils.logger import root_logger as logger from auth.internal import InternalAuthentication from auth import routes as auth_routes # Импортируем маршруты авторизации from auth.middleware import ( AuthorizationMiddleware, GraphQLExtensionsMiddleware, ) # Импортируем middleware для авторизации import_module("resolvers") import_module("auth.resolvers") # Создаем схему GraphQL schema = make_executable_schema(load_schema_from_path("schema/"), resolvers) # Пути к клиентским файлам CLIENT_DIR = join(os.path.dirname(__file__), "client") DIST_DIR = join(os.path.dirname(__file__), "dist") # Директория для собранных файлов INDEX_HTML = join(os.path.dirname(__file__), "index.html") async def index_handler(request: Request): """ Раздача основного HTML файла """ return FileResponse(INDEX_HTML) # GraphQL API class CustomGraphQLHTTPHandler(GraphQLHTTPHandler): """ Кастомный GraphQL HTTP обработчик, который добавляет объект response в контекст """ async def get_context_for_request(self, request: Request, data: dict) -> dict: """ Переопределяем метод для добавления объекта response и extensions в контекст """ context = await super().get_context_for_request(request, data) # Создаем объект ответа, который будем использовать для установки cookie response = JSONResponse({}) context["response"] = response # Добавляем extensions в контекст if "extensions" not in context: context["extensions"] = GraphQLExtensionsMiddleware() return context graphql_app = GraphQL(schema, debug=MODE == "development", http_handler=CustomGraphQLHTTPHandler()) async def graphql_handler(request): """Обработчик GraphQL запросов""" # Проверяем заголовок Content-Type content_type = request.headers.get("content-type", "") if not content_type.startswith("application/json") and "application/json" in request.headers.get( "accept", "" ): # Если не application/json, но клиент принимает JSON request._headers["content-type"] = "application/json" # Обрабатываем GraphQL запрос result = await graphql_app.handle_request(request) # Если result - это ответ от сервера, возвращаем его как есть if hasattr(result, "body"): return result # Если результат - это словарь, значит нужно его сконвертировать в JSONResponse if isinstance(result, dict): return JSONResponse(result) return result async def admin_handler(request: Request): """ Обработчик для маршрута /admin с серверной проверкой прав доступа """ # Проверяем авторизован ли пользователь if not request.user.is_authenticated: # Если пользователь не авторизован, перенаправляем на страницу входа return RedirectResponse(url="/login", status_code=303) # Проверяем является ли пользователь администратором auth = getattr(request, "auth", None) is_admin = False # Проверяем наличие объекта auth и метода is_admin if auth: try: # Проверяем имеет ли пользователь права администратора is_admin = auth.is_admin except Exception as e: logger.error(f"Ошибка при проверке прав администратора: {e}") # Дополнительная проверка email (для случаев, когда нет метода is_admin) admin_emails = ADMIN_EMAILS.split(",") if not is_admin and hasattr(auth, "email") and auth.email in admin_emails: is_admin = True if is_admin: # Если пользователь - администратор, возвращаем HTML-файл return FileResponse(INDEX_HTML) else: # Для авторизованных пользователей без прав администратора показываем страницу с ошибкой доступа return HTMLResponse( """
У вас нет прав для доступа к административной панели. Обратитесь к администратору системы для получения необходимых разрешений.
Вернуться на главную