259a1542d8
- Creates backend/services/audit.py with write_audit_log() function - Uses session.flush() not session.commit() per D-14 architectural requirement - Catches and logs all exceptions (never re-raises) so audit failure is non-fatal - Correct AuditLog ORM attribute metadata_ (not metadata) per models.py
59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
"""
|
|
Audit log service helper for DocuVault — Phase 4.
|
|
|
|
Provides write_audit_log(), a fire-and-forget helper called inline by API
|
|
handlers after successful operations (D-14).
|
|
|
|
Key architectural constraints:
|
|
- Uses session.flush() NOT session.commit() (D-14: the caller owns the
|
|
transaction; the audit entry is flushed within the same transaction but
|
|
the commit remains the caller's responsibility).
|
|
- NEVER raises — audit failure must not abort the primary operation.
|
|
Exceptions are logged at WARNING level and silently swallowed.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import uuid
|
|
from typing import Optional
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from db.models import AuditLog
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def write_audit_log(
|
|
session: AsyncSession,
|
|
event_type: str,
|
|
user_id: Optional[uuid.UUID],
|
|
actor_id: Optional[uuid.UUID],
|
|
resource_id: Optional[uuid.UUID],
|
|
ip_address: Optional[str],
|
|
metadata_: Optional[dict] = None,
|
|
) -> None:
|
|
"""Write an audit log entry within the caller's transaction.
|
|
|
|
Never raises — audit failure is non-fatal. Exceptions are caught and
|
|
logged as warnings so the primary operation is never aborted.
|
|
|
|
Uses session.flush() not session.commit() (D-14). The caller is responsible
|
|
for committing the transaction; this function merely ensures the audit row
|
|
is queued in the same unit of work.
|
|
"""
|
|
try:
|
|
entry = AuditLog(
|
|
event_type=event_type,
|
|
user_id=user_id,
|
|
actor_id=actor_id,
|
|
resource_id=resource_id,
|
|
ip_address=ip_address,
|
|
metadata_=metadata_,
|
|
)
|
|
session.add(entry)
|
|
await session.flush() # flush within handler's existing transaction, not commit
|
|
except Exception as exc:
|
|
logger.warning("audit log write failed: %s", exc)
|
|
# Do not re-raise — audit failure must never abort the primary operation
|