""" API endpoints for logging configuration and management. """ from flask import Blueprint, jsonify, request, send_file from auth import require_auth from config import config import logging import os from datetime import datetime logger = logging.getLogger(__name__) logging_bp = Blueprint('logging', __name__, url_prefix='/api/logging') @logging_bp.route('/config', methods=['GET']) @require_auth def get_logging_config(): """Get current logging configuration.""" try: # Import here to avoid circular imports from logging_config import logging_config as log_config config_data = { 'log_level': config.log_level, 'enable_console_logging': config.enable_console_logging, 'enable_console_progress': config.enable_console_progress, 'enable_fail2ban_logging': config.enable_fail2ban_logging, 'log_files': log_config.get_log_files() if hasattr(log_config, 'get_log_files') else [] } return jsonify({ 'success': True, 'config': config_data }) except Exception as e: logger.error(f"Error getting logging config: {e}") return jsonify({ 'success': False, 'error': str(e) }), 500 @logging_bp.route('/config', methods=['POST']) @require_auth def update_logging_config(): """Update logging configuration.""" try: data = request.get_json() or {} # Update log level log_level = data.get('log_level', config.log_level) if log_level in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']: config.log_level = log_level # Update console logging settings if 'enable_console_logging' in data: config.enable_console_logging = bool(data['enable_console_logging']) if 'enable_console_progress' in data: config.enable_console_progress = bool(data['enable_console_progress']) if 'enable_fail2ban_logging' in data: config.enable_fail2ban_logging = bool(data['enable_fail2ban_logging']) # Save configuration config.save_config() # Update runtime logging level try: from logging_config import logging_config as log_config log_config.update_log_level(config.log_level) except ImportError: # Fallback for basic logging numeric_level = getattr(logging, config.log_level.upper(), logging.INFO) logging.getLogger().setLevel(numeric_level) logger.info(f"Logging configuration updated: level={config.log_level}, console={config.enable_console_logging}") return jsonify({ 'success': True, 'message': 'Logging configuration updated successfully', 'config': { 'log_level': config.log_level, 'enable_console_logging': config.enable_console_logging, 'enable_console_progress': config.enable_console_progress, 'enable_fail2ban_logging': config.enable_fail2ban_logging } }) except Exception as e: logger.error(f"Error updating logging config: {e}") return jsonify({ 'success': False, 'error': str(e) }), 500 @logging_bp.route('/files', methods=['GET']) @require_auth def list_log_files(): """Get list of available log files.""" try: from logging_config import logging_config as log_config log_files = log_config.get_log_files() return jsonify({ 'success': True, 'files': log_files }) except Exception as e: logger.error(f"Error listing log files: {e}") return jsonify({ 'success': False, 'error': str(e) }), 500 @logging_bp.route('/files//download', methods=['GET']) @require_auth def download_log_file(filename): """Download a specific log file.""" try: # Security: Only allow log files if not filename.endswith('.log'): return jsonify({ 'success': False, 'error': 'Invalid file type' }), 400 log_directory = "logs" file_path = os.path.join(log_directory, filename) # Security: Check if file exists and is within log directory if not os.path.exists(file_path) or not os.path.abspath(file_path).startswith(os.path.abspath(log_directory)): return jsonify({ 'success': False, 'error': 'File not found' }), 404 return send_file( file_path, as_attachment=True, download_name=f"{filename}_{datetime.now().strftime('%Y%m%d_%H%M%S')}" ) except Exception as e: logger.error(f"Error downloading log file {filename}: {e}") return jsonify({ 'success': False, 'error': str(e) }), 500 @logging_bp.route('/files//tail', methods=['GET']) @require_auth def tail_log_file(filename): """Get the last N lines from a log file.""" try: # Security: Only allow log files if not filename.endswith('.log'): return jsonify({ 'success': False, 'error': 'Invalid file type' }), 400 lines = int(request.args.get('lines', 100)) lines = min(lines, 1000) # Limit to 1000 lines max log_directory = "logs" file_path = os.path.join(log_directory, filename) # Security: Check if file exists and is within log directory if not os.path.exists(file_path) or not os.path.abspath(file_path).startswith(os.path.abspath(log_directory)): return jsonify({ 'success': False, 'error': 'File not found' }), 404 # Read last N lines with open(file_path, 'r', encoding='utf-8') as f: all_lines = f.readlines() tail_lines = all_lines[-lines:] if len(all_lines) > lines else all_lines return jsonify({ 'success': True, 'lines': [line.rstrip('\n\r') for line in tail_lines], 'total_lines': len(all_lines), 'showing_lines': len(tail_lines) }) except Exception as e: logger.error(f"Error tailing log file {filename}: {e}") return jsonify({ 'success': False, 'error': str(e) }), 500 @logging_bp.route('/cleanup', methods=['POST']) @require_auth def cleanup_logs(): """Clean up old log files.""" try: data = request.get_json() or {} days = int(data.get('days', 30)) days = max(1, min(days, 365)) # Limit between 1-365 days from logging_config import logging_config as log_config cleaned_files = log_config.cleanup_old_logs(days) logger.info(f"Cleaned up {len(cleaned_files)} old log files (older than {days} days)") return jsonify({ 'success': True, 'message': f'Cleaned up {len(cleaned_files)} log files', 'cleaned_files': cleaned_files }) except Exception as e: logger.error(f"Error cleaning up logs: {e}") return jsonify({ 'success': False, 'error': str(e) }), 500 @logging_bp.route('/test', methods=['POST']) @require_auth def test_logging(): """Test logging at different levels.""" try: test_message = "Test log message from web interface" # Test different log levels logger.debug(f"DEBUG: {test_message}") logger.info(f"INFO: {test_message}") logger.warning(f"WARNING: {test_message}") logger.error(f"ERROR: {test_message}") # Test fail2ban logging try: from logging_config import log_auth_failure log_auth_failure("127.0.0.1", "test_user") except ImportError: pass # Test download progress logging try: from logging_config import log_download_progress log_download_progress("Test Series", "S01E01", 50.0, "1.2 MB/s", "5m 30s") except ImportError: pass return jsonify({ 'success': True, 'message': 'Test messages logged successfully' }) except Exception as e: logger.error(f"Error testing logging: {e}") return jsonify({ 'success': False, 'error': str(e) }), 500