feat: Implement a comprehensive audit log system with a dedicated dashboard page and backend API.
This commit is contained in:
93
backend/api/v1/modules/a76/audit_log/router.py
Normal file
93
backend/api/v1/modules/a76/audit_log/router.py
Normal file
@@ -0,0 +1,93 @@
|
||||
"""
|
||||
Audit Log Router
|
||||
"""
|
||||
from typing import List, Optional
|
||||
from datetime import date
|
||||
from fastapi import APIRouter, Depends, Query, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import or_, desc, distinct
|
||||
|
||||
from core.database import get_core_db
|
||||
from core.security import get_current_user # Assuming this exists
|
||||
from .models import AuditLog
|
||||
from .schemas import AuditLogListResponse, AuditLogResponse, AuditLogDetailResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/bitacora", response_model=AuditLogListResponse)
|
||||
async def get_bitacora(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(50, ge=1, le=100),
|
||||
search: Optional[str] = None,
|
||||
username: Optional[str] = None,
|
||||
procedure: Optional[str] = None,
|
||||
reference: Optional[str] = None,
|
||||
date_from: Optional[date] = None,
|
||||
date_to: Optional[date] = None,
|
||||
db: Session = Depends(get_core_db)
|
||||
):
|
||||
"""
|
||||
Get legacy audit log (Bitácora)
|
||||
"""
|
||||
query = db.query(AuditLog)
|
||||
|
||||
# Filters
|
||||
if date_from:
|
||||
query = query.filter(AuditLog.date >= date_from)
|
||||
if date_to:
|
||||
query = query.filter(AuditLog.date <= date_to)
|
||||
|
||||
if username:
|
||||
query = query.filter(AuditLog.username.ilike(f"%{username}%"))
|
||||
if procedure:
|
||||
# Exact match for dropdown filter usually better, but let's allow partial if manual
|
||||
# Legacy UI sends exact strings usually
|
||||
query = query.filter(AuditLog.procedure == procedure)
|
||||
if reference:
|
||||
query = query.filter(AuditLog.reference.ilike(f"%{reference}%"))
|
||||
|
||||
if search:
|
||||
# General search across main columns
|
||||
search_filter = or_(
|
||||
AuditLog.reference.ilike(f"%{search}%"),
|
||||
AuditLog.procedure.ilike(f"%{search}%"),
|
||||
AuditLog.movement.ilike(f"%{search}%"),
|
||||
AuditLog.username.ilike(f"%{search}%")
|
||||
)
|
||||
query = query.filter(search_filter)
|
||||
|
||||
total = query.count()
|
||||
|
||||
# Sort by ID desc (newest first) -> Legacy usually shows newest first or spec_id desc
|
||||
logs = query.order_by(desc(AuditLog.spec_id))\
|
||||
.offset((page - 1) * page_size)\
|
||||
.limit(page_size)\
|
||||
.all()
|
||||
|
||||
return {
|
||||
"data": logs,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size
|
||||
}
|
||||
|
||||
@router.get("/bitacora/procedimientos", response_model=List[str])
|
||||
async def get_procedures(db: Session = Depends(get_core_db)):
|
||||
"""
|
||||
Get distinct list of procedures for filters
|
||||
"""
|
||||
results = db.query(distinct(AuditLog.procedure))\
|
||||
.order_by(AuditLog.procedure)\
|
||||
.all()
|
||||
# verify if result is tuple
|
||||
return [r[0] for r in results if r[0]]
|
||||
|
||||
@router.get("/bitacora/{spec_id}/detalle", response_model=AuditLogDetailResponse)
|
||||
async def get_audit_detail(spec_id: int, db: Session = Depends(get_core_db)):
|
||||
"""
|
||||
Get full detail of a log entry
|
||||
"""
|
||||
log = db.query(AuditLog).filter(AuditLog.spec_id == spec_id).first()
|
||||
if not log:
|
||||
raise HTTPException(status_code=404, detail="Log entry not found")
|
||||
return log
|
||||
Reference in New Issue
Block a user