jebin2's picture
ref
cfe2de7
"""
Audit Service - Handle security and activity logging.
"""
import logging
from datetime import datetime
from typing import Optional, Dict, Any
from fastapi import Request
from sqlalchemy.ext.asyncio import AsyncSession
from core.models import AuditLog
logger = logging.getLogger(__name__)
class AuditService:
"""
Service for creating audit logs.
"""
@staticmethod
async def log_event(
db: AsyncSession,
action: str,
status: str,
user_id: Optional[int] = None,
client_user_id: Optional[str] = None,
details: Optional[Dict[str, Any]] = None,
request: Optional[Request] = None,
error_message: Optional[str] = None,
log_type: str = "client"
) -> AuditLog:
"""
Create an audit log entry.
Args:
db: Database session
action: Event action (e.g., "login", "logout")
status: Outcome (e.g., "success", "failure")
user_id: ID of the authenticated user (if any)
client_user_id: Client-side ID (if any)
details: Additional metadata
request: FastAPI request object (for IP/UA)
error_message: Error description if failed
log_type: "client" or "server"
Returns:
The created AuditLog instance
"""
ip_address = None
user_agent = None
refer_url = None
if request:
ip_address = request.client.host if request.client else None
user_agent = request.headers.get("user-agent")
refer_url = request.headers.get("referer")
# Create log entry
audit_log = AuditLog(
log_type=log_type,
user_id=user_id,
client_user_id=client_user_id,
action=action,
details=details or {},
ip_address=ip_address,
user_agent=user_agent,
refer_url=refer_url,
status=status,
error_message=error_message,
timestamp=datetime.utcnow()
)
db.add(audit_log)
# We don't commit here to allow the caller to group with other transactions
# or commit explicitly.
logger.info(f"Audit Log: [{status}] {action} - User: {user_id or 'Anon'} - IP: {ip_address}")
return audit_log
# Global instance not strictly needed as it's a static method helper,
# but keeping pattern consistent if we add state later.
audit_service = AuditService()