Rename AdminLog to AuditLog for better semantic clarity

The table name 'admin_logs' was misleading as it contains both admin actions
and automated system events. Renamed to 'audit_logs' to better reflect its
purpose as a comprehensive audit trail.

Changes:
- Renamed model: AdminLog → AuditLog (with backwards compatibility alias)
- Database migration: Renames admin_logs table to audit_logs (idempotent)
- Updated admin_logging.py: log_admin_event → log_audit_event (with alias)
- Updated imports in routes_core.py and routes_shared.py
- Updated all references to use AuditLog instead of AdminLog
- Backwards compatibility maintained via aliases for smooth transition

This is part 1 of audit logging expansion.
Part 2 will add logging for settings changes and export/import actions.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Ivo Oskamp 2026-02-07 00:25:47 +01:00
parent b27e8db602
commit 4c0b5ada37
5 changed files with 67 additions and 12 deletions

View File

@ -6,10 +6,10 @@ from typing import Optional
from flask_login import current_user
from .database import db
from .models import AdminLog
from .models import AuditLog
def log_admin_event(
def log_audit_event(
event_type: str,
message: str,
details: Optional[str] = None,
@ -17,7 +17,7 @@ def log_admin_event(
username: Optional[str] = None,
commit: bool = True,
) -> None:
"""Write an entry to the in-app AdminLog table.
"""Write an entry to the in-app AuditLog table.
- This is the source for the /logging page in the website (not container logs).
- Retention: keep only the last 7 days.
@ -30,7 +30,7 @@ def log_admin_event(
except Exception:
username = None
entry = AdminLog(
entry = AuditLog(
user=username,
event_type=(event_type or "event")[:64],
message=(message or "")[:2000],
@ -41,7 +41,7 @@ def log_admin_event(
# Enforce retention: keep only the last 7 days
try:
cutoff = datetime.utcnow() - timedelta(days=7)
AdminLog.query.filter(AdminLog.created_at < cutoff).delete(synchronize_session=False)
AuditLog.query.filter(AuditLog.created_at < cutoff).delete(synchronize_session=False)
except Exception:
# Never block the main action because of retention cleanup.
pass
@ -54,3 +54,7 @@ def log_admin_event(
except Exception:
# If logging fails, do not raise and do not print to container logs.
db.session.rollback()
# Legacy alias for backwards compatibility
log_admin_event = log_audit_event

View File

@ -230,7 +230,7 @@ def dashboard():
@login_required
@roles_required("admin", "operator")
def logging_page():
# Server-side view of AdminLog entries.
# Server-side view of AuditLog entries (system audit trail).
try:
page = int(request.args.get("page", "1"))
except ValueError:
@ -239,7 +239,7 @@ def logging_page():
page = 1
per_page = 20
query = AdminLog.query.order_by(AdminLog.created_at.desc().nullslast(), AdminLog.id.desc())
query = AuditLog.query.order_by(AuditLog.created_at.desc().nullslast(), AuditLog.id.desc())
total_items = query.count()
total_pages = max(1, math.ceil(total_items / per_page)) if total_items else 1
if page > total_pages:

View File

@ -34,7 +34,7 @@ from ..job_matching import build_job_match_key, find_matching_job
from ..database import db
from ..models import (
SystemSettings,
AdminLog,
AuditLog,
Customer,
Job,
JobRun,
@ -597,7 +597,7 @@ def _log_admin_event(event_type: str, message: str, details: str | None = None)
except Exception:
username = None
entry = AdminLog(
entry = AuditLog(
user=username,
event_type=event_type,
message=message,
@ -608,7 +608,7 @@ def _log_admin_event(event_type: str, message: str, details: str | None = None)
# Enforce retention: keep only the last 7 days
try:
cutoff = datetime.utcnow() - timedelta(days=7)
AdminLog.query.filter(AdminLog.created_at < cutoff).delete(synchronize_session=False)
AuditLog.query.filter(AuditLog.created_at < cutoff).delete(synchronize_session=False)
except Exception:
# If cleanup fails we still try to commit the new entry
pass

View File

@ -1024,6 +1024,52 @@ def migrate_news_tables() -> None:
print("[migrations] migrate_news_tables completed.")
def migrate_rename_admin_logs_to_audit_logs() -> None:
"""Rename admin_logs table to audit_logs for better semantic clarity.
The table name 'admin_logs' was misleading as it contains both admin actions
and automated system events. The new name 'audit_logs' better reflects its
purpose as a comprehensive audit trail for both user actions and system events.
This migration is idempotent and safe to run multiple times.
"""
engine = db.get_engine()
with engine.begin() as conn:
# Check if old table exists and new table doesn't
old_exists = conn.execute(
text(
"""
SELECT 1 FROM information_schema.tables
WHERE table_name = 'admin_logs'
LIMIT 1
"""
)
).first() is not None
new_exists = conn.execute(
text(
"""
SELECT 1 FROM information_schema.tables
WHERE table_name = 'audit_logs'
LIMIT 1
"""
)
).first() is not None
if old_exists and not new_exists:
# Rename the table
conn.execute(text("ALTER TABLE admin_logs RENAME TO audit_logs"))
print("[migrations] Renamed admin_logs → audit_logs")
elif new_exists and not old_exists:
print("[migrations] audit_logs already exists (admin_logs already migrated)")
elif old_exists and new_exists:
# Both exist - this shouldn't happen but handle gracefully
print("[migrations] WARNING: Both admin_logs and audit_logs exist. Manual intervention may be needed.")
else:
# Neither exists - table will be created by db.create_all()
print("[migrations] audit_logs table will be created by db.create_all()")
def run_migrations() -> None:
print("[migrations] Starting migrations...")
migrate_add_username_to_users()
@ -1064,6 +1110,7 @@ def run_migrations() -> None:
migrate_reporting_report_config()
migrate_performance_indexes()
migrate_system_settings_require_daily_dashboard_visit()
migrate_rename_admin_logs_to_audit_logs()
print("[migrations] All migrations completed.")

View File

@ -146,8 +146,8 @@ class SystemSettings(db.Model):
class AdminLog(db.Model):
__tablename__ = "admin_logs"
class AuditLog(db.Model):
__tablename__ = "audit_logs"
id = db.Column(db.Integer, primary_key=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
@ -157,6 +157,10 @@ class AdminLog(db.Model):
message = db.Column(db.Text, nullable=False)
details = db.Column(db.Text, nullable=True)
# Legacy alias for backwards compatibility during migration
AdminLog = AuditLog
class Customer(db.Model):
__tablename__ = "customers"