mathpulse-api-v3test / services /audit_logger.py
github-actions[bot]
🚀 Auto-deploy backend from GitHub (9923591)
c1d887c
import logging
import traceback
from datetime import datetime, timezone
from typing import Optional, Dict, Any
from firebase_admin import firestore
logger = logging.getLogger("audit_logger")
async def log_audit_event(
action: str,
actor_uid: str,
actor_name: str,
actor_email: str,
actor_role: str,
description: str,
target_type: Optional[str] = None,
target_id: Optional[str] = None,
target_name: Optional[str] = None,
route: Optional[str] = None,
module: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
success: bool = True,
) -> None:
"""
Log an audit event to the accessAuditLogs collection.
Fails safely - will not crash the app if logging fails.
"""
try:
# Sanitize metadata to remove potential secrets
safe_metadata = {}
if metadata:
for k, v in metadata.items():
if "password" in k.lower() or "token" in k.lower() or "secret" in k.lower():
safe_metadata[k] = "[REDACTED]"
else:
safe_metadata[k] = v
db = firestore.client()
log_entry = {
"timestamp": firestore.SERVER_TIMESTAMP,
"action": action,
"actorUid": actor_uid,
"actorName": actor_name,
"actorEmail": actor_email,
"actorRole": actor_role,
"description": description,
"success": success,
}
if target_type is not None:
log_entry["targetType"] = target_type
if target_id is not None:
log_entry["targetId"] = target_id
if target_name is not None:
log_entry["targetName"] = target_name
if route is not None:
log_entry["route"] = route
if module is not None:
log_entry["module"] = module
if safe_metadata:
log_entry["metadata"] = safe_metadata
db.collection("accessAuditLogs").add(log_entry)
except Exception as e:
logger.error(f"Failed to write audit log ({action}): {str(e)}")
# We catch and log, but do not raise, so the main app flow continues