feat: Implement a comprehensive audit log system with a dedicated dashboard page and backend API.
This commit is contained in:
116
backend/api/v1/modules/a76/audit_log/events.py
Normal file
116
backend/api/v1/modules/a76/audit_log/events.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
Audit Log Events
|
||||
"""
|
||||
from sqlalchemy import event, inspect
|
||||
from sqlalchemy.orm import Session
|
||||
from .services.service import AuditService
|
||||
from core.context import get_user_context
|
||||
|
||||
def register_audit_listeners(models_to_audit):
|
||||
"""
|
||||
Register SQLAlchemy listeners for given models
|
||||
"""
|
||||
for model in models_to_audit:
|
||||
event.listen(model, "after_insert", after_insert_listener)
|
||||
event.listen(model, "after_update", after_update_listener)
|
||||
event.listen(model, "after_delete", after_delete_listener)
|
||||
|
||||
def _get_current_username():
|
||||
try:
|
||||
context = get_user_context()
|
||||
if context:
|
||||
# Token usually has 'preferred_username' or 'name' or 'sub'
|
||||
return context.get("preferred_username") or context.get("email") or context.get("sub") or "System"
|
||||
except:
|
||||
pass
|
||||
return "System"
|
||||
|
||||
def after_insert_listener(mapper, connection, target):
|
||||
"""
|
||||
Listener for INSERT operations
|
||||
"""
|
||||
table_name = target.__tablename__
|
||||
record_data = {c.name: getattr(target, c.name) for c in mapper.columns}
|
||||
username = _get_current_username()
|
||||
company_id = getattr(target, "company_id", None)
|
||||
|
||||
# Create a session bound to the connection
|
||||
session = Session(bind=connection)
|
||||
try:
|
||||
AuditService.log_crud_operation(
|
||||
db=session,
|
||||
table_name=table_name,
|
||||
operation_type="CREATE",
|
||||
record_data=record_data,
|
||||
username=username,
|
||||
record_id=str(getattr(target, "id", "")),
|
||||
company_id=company_id
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error logging insert: {e}")
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
def after_update_listener(mapper, connection, target):
|
||||
"""
|
||||
Listener for UPDATE operations
|
||||
"""
|
||||
table_name = target.__tablename__
|
||||
|
||||
state = inspect(target)
|
||||
changes = {}
|
||||
old_values = {}
|
||||
new_values = {}
|
||||
|
||||
for attr in state.attrs:
|
||||
hist = attr.history
|
||||
if hist.has_changes():
|
||||
changes[attr.key] = hist.added[0] if hist.added else None
|
||||
old_values[attr.key] = hist.deleted[0] if hist.deleted else None
|
||||
new_values[attr.key] = hist.added[0] if hist.added else None
|
||||
|
||||
if not changes:
|
||||
return
|
||||
|
||||
record_data = {c.name: getattr(target, c.name) for c in mapper.columns}
|
||||
username = _get_current_username()
|
||||
|
||||
session = Session(bind=connection)
|
||||
try:
|
||||
AuditService.log_crud_operation(
|
||||
db=session,
|
||||
table_name=table_name,
|
||||
operation_type="UPDATE",
|
||||
record_data=record_data,
|
||||
username=username,
|
||||
record_id=str(getattr(target, "id", "")),
|
||||
old_values=old_values,
|
||||
new_values=new_values
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error logging update: {e}")
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
def after_delete_listener(mapper, connection, target):
|
||||
"""
|
||||
Listener for DELETE operations
|
||||
"""
|
||||
table_name = target.__tablename__
|
||||
record_data = {c.name: getattr(target, c.name) for c in mapper.columns}
|
||||
username = _get_current_username()
|
||||
|
||||
session = Session(bind=connection)
|
||||
try:
|
||||
AuditService.log_crud_operation(
|
||||
db=session,
|
||||
table_name=table_name,
|
||||
operation_type="DELETE",
|
||||
record_data=record_data,
|
||||
username=username,
|
||||
record_id=str(getattr(target, "id", ""))
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error logging delete: {e}")
|
||||
finally:
|
||||
session.close()
|
||||
Reference in New Issue
Block a user