import json import logging from asyncio import CancelledError from typing import Callable import sentry_sdk from aiohttp import web import config import context from server.core.exceptions import APIError, BadGateway, Unauthorized from server.core.utils import set_debug_data_source, set_debug_request_count, set_debug_retry_stats logger = logging.getLogger("app") @web.middleware async def auth_middleware(request, handler): """Authenticate a user. Authentication is performed by extracting AUTH_KEY from header Returns: None Raises: Unauthorized: If authorization token is invalid or missing. """ if request.rel_url.path == request.app.router["health"].url_for().path: return await handler(request) if config.DEBUG: if request.path.startswith("/api/doc") or request.path.startswith("/static"): return await handler(request) api_key = request.headers.get("Authorization") if not api_key: raise Unauthorized("Authorization key is not provided.") if api_key != config.VENDORAPI_APPKEY: raise Unauthorized("Invalid authorization key.") return await handler(request) @web.middleware async def error_middleware(request: web.Request, handler: Callable) -> web.Response: """Callback for handling exceptions""" try: return await handler(request) except APIError as ex: if ex.status_code not in config.SENTRY_IGNORE_STATUS_CODES: sentry_sdk.capture_exception(ex) return ex.respond() except CancelledError: return BadGateway(detail="Request was cancelled.").respond() @web.middleware async def logging_middleware(request: web.Request, handler: Callable) -> web.Response: """Callback for logging requests""" response = await handler(request) logger.info(f"{request.method} - {request.rel_url} - {response.status}") return response @web.middleware async def header_middleware(request: web.Request, handler: Callable) -> web.Response: """Callback for logging requests""" timeout = request.headers.get("X-Data-Timeout", config.DEFAULT_DATA_TIMEOUT) if str(timeout).lstrip("-").isdigit(): context.DATA_TIMEOUT.set(int(timeout)) context.REQUEST.set(request) response = await handler(request) headers = {} if config.DEBUG_DATA_SOURCE: headers["X-Data-Source"] = set_debug_data_source(force_replace=False) if config.DEBUG_RETRY_STATS: headers["X-Retry-Stats"] = set_debug_retry_stats(force_replace=False) if config.DEBUG_REQUEST_COUNT: headers["X-Request-Count"] = set_debug_request_count(force_replace=False) if config.DEBUG_EXECUTION_TIME: headers["X-Execution-Time"] = json.dumps({"cache": context.CACHE_TIME.get(), "api": context.REQUEST_TIME.get()}) response.headers.update(headers) return response