"""Audit trail API (admin-only). Not added to app._PEER_READABLE_PATHS, so enforce_auth blocks peer-role sessions with 403. Routes are thin — all logic lives in AuditManager. """ import logging from flask import Blueprint, request, jsonify, Response logger = logging.getLogger('picell') bp = Blueprint('audit', __name__) def _filters_from_args(): args = request.args filters = {} for field in ('actor', 'action', 'target_type', 'target_id', 'result', 'since', 'until'): val = args.get(field) if val: filters[field] = val return filters @bp.route('/api/audit', methods=['GET']) def list_audit(): try: from app import audit_manager try: limit = int(request.args.get('limit', 100)) except (TypeError, ValueError): limit = 100 try: offset = int(request.args.get('offset', 0)) except (TypeError, ValueError): offset = 0 result = audit_manager.query(_filters_from_args(), limit=limit, offset=offset) return jsonify(result) except Exception as e: logger.error(f"list_audit: {e}") return jsonify({'error': str(e)}), 500 @bp.route('/api/audit/export', methods=['GET']) def export_audit(): try: from app import audit_manager fmt = request.args.get('format', 'csv') if fmt != 'csv': return jsonify({'error': 'only csv format is supported'}), 400 csv_text = audit_manager.export_csv(_filters_from_args()) return Response( csv_text, mimetype='text/csv', headers={'Content-Disposition': 'attachment; filename="audit.csv"'}, ) except Exception as e: logger.error(f"export_audit: {e}") return jsonify({'error': str(e)}), 500 @bp.route('/api/audit/verify', methods=['GET']) def verify_audit(): try: from app import audit_manager return jsonify(audit_manager.verify_chain()) except Exception as e: logger.error(f"verify_audit: {e}") return jsonify({'error': str(e)}), 500