"""Audit log API routes.""" from flask import Blueprint, request from flask_jwt_extended import jwt_required from shopdb.core.models import AuditLog from shopdb.utils.responses import success_response, error_response, ErrorCodes auditlogs_bp = Blueprint('auditlogs', __name__) @auditlogs_bp.route('', methods=['GET']) @jwt_required() def list_auditlogs(): """ List audit logs with filtering and pagination. Query params: page: Page number (default 1) perpage: Items per page (default 50, max 200) action: Filter by action (created, updated, deleted) entitytype: Filter by entity type userid: Filter by user ID search: Search in entityname or username from_date: Filter from date (ISO format) to_date: Filter to date (ISO format) """ page = request.args.get('page', 1, type=int) perpage = min(request.args.get('perpage', 50, type=int), 200) query = AuditLog.query # Filters action = request.args.get('action') if action: query = query.filter(AuditLog.action == action) entitytype = request.args.get('entitytype') if entitytype: query = query.filter(AuditLog.entitytype == entitytype) userid = request.args.get('userid', type=int) if userid: query = query.filter(AuditLog.userid == userid) search = request.args.get('search') if search: search_term = f'%{search}%' query = query.filter( (AuditLog.entityname.ilike(search_term)) | (AuditLog.username.ilike(search_term)) ) from_date = request.args.get('from_date') if from_date: from datetime import datetime try: dt = datetime.fromisoformat(from_date.replace('Z', '+00:00')) query = query.filter(AuditLog.timestamp >= dt) except ValueError: pass to_date = request.args.get('to_date') if to_date: from datetime import datetime try: dt = datetime.fromisoformat(to_date.replace('Z', '+00:00')) query = query.filter(AuditLog.timestamp <= dt) except ValueError: pass # Order by most recent first query = query.order_by(AuditLog.timestamp.desc()) # Paginate pagination = query.paginate(page=page, per_page=perpage, error_out=False) return success_response( [log.to_dict() for log in pagination.items], meta={ 'page': page, 'perpage': perpage, 'total': pagination.total, 'pages': pagination.pages } ) @auditlogs_bp.route('/entity//', methods=['GET']) @jwt_required() def get_entity_history(entitytype: str, entityid: int): """Get audit history for a specific entity.""" logs = AuditLog.query.filter_by( entitytype=entitytype, entityid=entityid ).order_by(AuditLog.timestamp.desc()).all() return success_response([log.to_dict() for log in logs]) @auditlogs_bp.route('/stats', methods=['GET']) @jwt_required() def get_stats(): """Get audit log statistics.""" from sqlalchemy import func from datetime import datetime, timedelta # Actions by type actions = db_func_count_by(AuditLog.action) # Entity types entities = db_func_count_by(AuditLog.entitytype) # Recent activity (last 7 days) week_ago = datetime.utcnow() - timedelta(days=7) recent_count = AuditLog.query.filter(AuditLog.timestamp >= week_ago).count() # Most active users (last 7 days) from shopdb.extensions import db active_users = db.session.query( AuditLog.username, func.count(AuditLog.auditlogid).label('count') ).filter( AuditLog.timestamp >= week_ago, AuditLog.username.isnot(None) ).group_by(AuditLog.username).order_by(func.count(AuditLog.auditlogid).desc()).limit(5).all() return success_response({ 'actions': actions, 'entities': entities, 'recentCount': recent_count, 'activeUsers': [{'username': u[0], 'count': u[1]} for u in active_users] }) def db_func_count_by(column): """Helper to count grouped by a column.""" from sqlalchemy import func from shopdb.extensions import db results = db.session.query( column, func.count().label('count') ).group_by(column).all() return {r[0]: r[1] for r in results}