from services.log import access_logger import time from typing import Optional from fastapi import Request from starlette.middleware.base import BaseHTTPMiddleware from nicegui import app import uuid def get_client_ip(request: Request) -> str: """Extract client IP address with X-Forwarded-For support""" # Check X-Forwarded-For header first (for reverse proxies) forwarded_for = request.headers.get('X-Forwarded-For') if forwarded_for: # Take the first IP in the chain (original client) return forwarded_for.split(',')[0].strip() # Check X-Real-IP header (nginx) real_ip = request.headers.get('X-Real-IP') if real_ip: return real_ip.strip() # Fall back to direct connection IP return request.client.host if request.client else 'unknown' def get_user_agent(request: Request) -> str: """Extract user agent string""" return request.headers.get('User-Agent', 'unknown') def get_current_user() -> Optional[str]: """Get current authenticated user""" if app.storage.user.get('authenticated', False): return app.storage.user.get('username', 'authenticated_user') return None class AccessLoggingMiddleware(BaseHTTPMiddleware): """Middleware to log all HTTP requests with client IP and user info""" async def dispatch(self, request: Request, call_next): start_time = time.time() request_id = str(uuid.uuid4()) # Generate a unique request ID client_ip = get_client_ip(request) user_agent = get_user_agent(request) user = get_current_user() # Process the request response = await call_next(request) # Calculate processing time process_time = time.time() - start_time # Log the access log_message = ( f"{request.method} {request.url.path} " f"{response.status_code} " f"\"{user_agent}\"" ) access_logger.extra = { 'request_id': request_id, 'client_ip': client_ip, 'user_agent': user_agent, 'user': user if user else 'anonymous', 'process_time': f"{process_time:.3f}s", } # Use different log levels based on status code if response.status_code >= 500: access_logger.error(log_message) elif response.status_code >= 400: access_logger.warning(log_message) else: access_logger.info(log_message) return response