from fastapi import Request, status, HTTPException from fastapi.responses import JSONResponse from webapi.middleware.freeleaps_auth_middleware import request_context_var from common.log.module_logger import ModuleLogger class DatabaseMiddleware: def __init__(self, app): self.app = app self.module_logger = ModuleLogger(sender_id=DatabaseMiddleware) async def __call__(self, scope, receive, send): if scope["type"] != "http": return await self.app(scope, receive, send) request = Request(scope, receive) # Get tenant id from auth context (set by FreeleapsAuthMiddleware) product_id = None try: ctx = request_context_var.get() product_id = getattr(ctx, "product_id", None) await self.module_logger.log_info(f"Retrieved product_id from auth context: {product_id}") except Exception as e: await self.module_logger.log_error(f"Failed to get auth context: {str(e)}") product_id = None # Get tenant cache and main database from app state try: tenant_cache = request.app.state.tenant_cache main_db = request.app.state.main_db await self.module_logger.log_info(f"Retrieved app state - tenant_cache: {'success' if tenant_cache is not None else 'fail'}, main_db: {'success' if main_db is not None else 'fail'}") except Exception as e: await self.module_logger.log_error(f"Failed to get app state: {str(e)}") response = JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"detail": "Database not properly initialized"} ) return await response(scope, receive, send) if not product_id: # Compatibility / public routes: use main database with BaseDoc context set await self.module_logger.log_info(f"No product_id - using main database for path: {request.url.path}") # Get main database with BaseDoc context set for all models main_db_initialized = await tenant_cache.get_main_db_initialized() request.state.db = main_db_initialized request.state.product_id = None await self.module_logger.log_info(f"Successfully set BaseDoc context for main database") return await self.app(scope, receive, send) try: # Get tenant-specific database with BaseDoc context set (cached) await self.module_logger.log_info(f"Attempting to get tenant database for product_id: {product_id}") tenant_db = await tenant_cache.get_initialized_db(product_id) request.state.db = tenant_db request.state.product_id = product_id await self.module_logger.log_info(f"Successfully retrieved cached tenant database with BaseDoc context for product_id: {product_id}") return await self.app(scope, receive, send) except ValueError as e: # Handle tenant not found or inactive (ValueError from TenantDBCache) await self.module_logger.log_error(f"Tenant error for {product_id}: {str(e)}") response = JSONResponse( status_code=status.HTTP_404_NOT_FOUND, content={"detail": str(e)} ) return await response(scope, receive, send) except Exception as e: await self.module_logger.log_error(f"Database error for tenant {product_id}: {str(e)}") response = JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"detail": "Database connection error"} ) return await response(scope, receive, send) return await self.app(scope, receive, send)