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:
parent
b27e8db602
commit
4c0b5ada37
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.")
|
||||
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user