From 27f998457453bc5b55805f6cbec51a19e96cf97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Tue, 4 Nov 2025 08:26:41 +0100 Subject: [PATCH] fixes --- app.py | 80 ++++++++++++------- static/js/logs.js | 198 +++++++++++++++++++++++----------------------- 2 files changed, 153 insertions(+), 125 deletions(-) diff --git a/app.py b/app.py index 2027b2f..2378017 100644 --- a/app.py +++ b/app.py @@ -1,10 +1,8 @@ - import os import sys import ssl - import configparser -from flask import Flask, render_template, render_template_string +from flask import Flask, render_template, render_template_string, request, jsonify from routes.main_routes import main_bp from routes.edit_routes import edit_bp from utils.stats_utils import fetch_haproxy_stats, parse_haproxy_stats @@ -59,7 +57,6 @@ except Exception as e: app.register_blueprint(main_bp) app.register_blueprint(edit_bp) - setup_auth(app) certificate_path = None @@ -69,71 +66,98 @@ ssl_context = None try: config2 = configparser.ConfigParser() config2.read(SSL_INI) - if config2.has_section('ssl'): certificate_path = config2.get('ssl', 'certificate_path') private_key_path = config2.get('ssl', 'private_key_path') else: print(f"[APP] No [ssl] section in {SSL_INI}", flush=True) sys.exit(1) - + if not os.path.exists(certificate_path): print(f"[APP] Certificate not found: {certificate_path}", flush=True) sys.exit(1) - + if not os.path.exists(private_key_path): print(f"[APP] Private key not found: {private_key_path}", flush=True) sys.exit(1) - + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) ssl_context.load_cert_chain(certfile=certificate_path, keyfile=private_key_path) print(f"[APP] SSL context loaded", flush=True) - except Exception as e: print(f"[APP] SSL error: {e}", flush=True) sys.exit(1) - @app.route('/statistics') def display_haproxy_stats(): haproxy_stats = fetch_haproxy_stats() parsed_stats = parse_haproxy_stats(haproxy_stats) return render_template('statistics.html', stats=parsed_stats) - @app.route('/logs', endpoint='display_logs') -#@requires_auth def display_haproxy_logs(): log_file_path = '/var/log/haproxy.log' - if not os.path.exists(log_file_path): - return render_template('logs.html', - logs=[], + return render_template('logs.html', + logs=[], + total_logs=0, error_message=f"Log file not found: {log_file_path}") try: logs = parse_log_file(log_file_path) - if not logs: - return render_template('logs.html', - logs=[], - error_message="Log file is empty or unreadable") - return render_template('logs.html', logs=logs) - except Exception as e: + total_logs = len(logs) + # Załaduj ostatnie 200 logów + initial_logs = logs[-200:] if len(logs) > 200 else logs + return render_template('logs.html', - logs=[], + logs=initial_logs, + total_logs=total_logs, + loaded_count=len(initial_logs)) + except Exception as e: + return render_template('logs.html', + logs=[], + total_logs=0, error_message=f"Error parsing logs: {str(e)}") +@app.route('/api/logs', methods=['POST']) +def api_get_logs(): + try: + log_file_path = '/var/log/haproxy.log' + + if not os.path.exists(log_file_path): + return jsonify({'error': 'Log file not found'}), 404 + + page = request.json.get('page', 1) + per_page = request.json.get('per_page', 50) + offset = (page - 1) * per_page + + logs = parse_log_file(log_file_path) + total_logs = len(logs) + + reversed_logs = logs[::-1] + paginated_logs = reversed_logs[offset:offset + per_page] + + return jsonify({ + 'success': True, + 'logs': paginated_logs, + 'page': page, + 'per_page': per_page, + 'total': total_logs, + 'has_more': offset + per_page < total_logs + }) + except Exception as e: + print(f"[API] Error: {e}", flush=True) + return jsonify({'error': str(e), 'success': False}), 500 @app.route('/home') def home(): frontend_count, backend_count, acl_count, layer7_count, layer4_count = count_frontends_and_backends() - return render_template('home.html', - frontend_count=frontend_count, - backend_count=backend_count, + return render_template('home.html', + frontend_count=frontend_count, + backend_count=backend_count, acl_count=acl_count, - layer7_count=layer7_count, + layer7_count=layer7_count, layer4_count=layer4_count) - if __name__ == '__main__': - app.run(host='::', port=5000, ssl_context=ssl_context, debug=True) \ No newline at end of file + app.run(host='::', port=5000, ssl_context=ssl_context, debug=True) diff --git a/static/js/logs.js b/static/js/logs.js index d0d790f..4ccc8de 100644 --- a/static/js/logs.js +++ b/static/js/logs.js @@ -1,103 +1,107 @@ document.addEventListener('DOMContentLoaded', function() { - const filterIp = document.getElementById('filter_ip'); - const filterStatus = document.getElementById('filter_status'); - const filterMethod = document.getElementById('filter_method'); - const filterThreats = document.getElementById('filter_threats'); - const filterHideStats = document.getElementById('filter_hide_stats'); - const resetBtn = document.getElementById('reset_filters'); + let currentPage = 1; + let perPage = 50; + let totalLogs = parseInt(document.getElementById('total_count').textContent); + let allLogs = []; - const logsTable = document.getElementById('logs_table'); - if (!logsTable) return; // Exit if no logs + const logsContainer = document.getElementById('logs_container'); + const perPageSelect = document.getElementById('logs_per_page'); + const refreshBtn = document.getElementById('refresh_logs_btn'); + const prevBtn = document.getElementById('prev_btn'); + const nextBtn = document.getElementById('next_btn'); + const loadAllBtn = document.getElementById('load_all_btn'); + const loadedSpan = document.getElementById('loaded_count'); + const currentPageSpan = document.getElementById('current_page'); + const totalPagesSpan = document.getElementById('total_pages'); - const allRows = Array.from(document.querySelectorAll('.log-row')); - - // Filter function - function applyFilters() { - const ipValue = filterIp.value.toLowerCase(); - const statusValue = filterStatus.value; - const methodValue = filterMethod.value; - const showThreats = filterThreats.checked; - const hideStats = filterHideStats.checked; - - let visibleCount = 0; - let threatCount = 0; - let count2xx = 0, count4xx = 0, count5xx = 0; - const uniqueIps = new Set(); - - allRows.forEach(row => { - const ip = row.dataset.ip; - const status = row.dataset.status; - const method = row.dataset.method; - const hasThreat = row.dataset.threats === '1'; - const url = row.querySelector('td:nth-child(4)').textContent.trim(); - - let show = true; - - // IP filter - if (ipValue && !ip.includes(ipValue)) { - show = false; - } - - // Status filter - if (statusValue) { - const statusStart = statusValue; - if (!status.startsWith(statusStart)) { - show = false; - } - } - - // Method filter - if (methodValue && method !== methodValue) { - show = false; - } - - // Threats filter - if (!showThreats && hasThreat) { - show = false; - } - - // Hide /stats filter - if (hideStats && url.includes('/stats')) { - show = false; - } - - row.style.display = show ? '' : 'none'; - - if (show) { - visibleCount++; - if (hasThreat) threatCount++; - if (status.startsWith('2')) count2xx++; - if (status.startsWith('4')) count4xx++; - if (status.startsWith('5')) count5xx++; - uniqueIps.add(ip); - } - }); - - // Update stats - document.getElementById('stat_total').textContent = visibleCount; - document.getElementById('stat_threats').textContent = threatCount; - document.getElementById('stat_2xx').textContent = count2xx; - document.getElementById('stat_4xx').textContent = count4xx; - document.getElementById('stat_5xx').textContent = count5xx; - document.getElementById('stat_ips').textContent = uniqueIps.size; - } - - // Event listeners - filterIp.addEventListener('input', applyFilters); - filterStatus.addEventListener('change', applyFilters); - filterMethod.addEventListener('change', applyFilters); - filterThreats.addEventListener('change', applyFilters); - filterHideStats.addEventListener('change', applyFilters); - - // Reset button - resetBtn.addEventListener('click', function() { - filterIp.value = ''; - filterStatus.value = ''; - filterMethod.value = ''; - filterThreats.checked = true; - filterHideStats.checked = true; - applyFilters(); + perPageSelect.addEventListener('change', function(e) { + perPage = parseInt(e.target.value); + currentPage = 1; + loadLogs(); }); - applyFilters(); + refreshBtn.addEventListener('click', function() { + currentPage = 1; + loadLogs(); + }); + + prevBtn.addEventListener('click', function() { + if (currentPage > 1) { + currentPage--; + loadLogs(); + } + }); + + nextBtn.addEventListener('click', function() { + const totalPages = Math.ceil(totalLogs / perPage); + if (currentPage < totalPages) { + currentPage++; + loadLogs(); + } + }); + + loadAllBtn.addEventListener('click', function() { + perPage = totalLogs; + currentPage = 1; + perPageSelect.value = totalLogs; + loadLogs(); + }); + + function loadLogs() { + fetch('/api/logs', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + page: currentPage, + per_page: perPage + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + renderLogs(data.logs); + updatePagination(data); + console.log(`[Logs] Załadowano page ${data.page}/${Math.ceil(data.total / data.per_page)}`); + } + }) + .catch(error => { + console.error('Error loading logs:', error); + logsContainer.innerHTML = `Błąd załadowania logów`; + }); + } + + function renderLogs(logs) { + if (!logs || logs.length === 0) { + logsContainer.innerHTML = ' Brak logów'; + return; + } + + logsContainer.innerHTML = logs.map(entry => ` + + + + ${entry.timestamp || 'N/A'}
+ ${entry.source || 'N/A'}
+ + ${entry.message || 'N/A'} + +
+ + + `).join(''); + } + + function updatePagination(data) { + const totalPages = Math.ceil(data.total / data.per_page); + loadedSpan.textContent = data.logs.length; + currentPageSpan.textContent = data.page; + totalPagesSpan.textContent = totalPages; + + prevBtn.disabled = data.page === 1; + nextBtn.disabled = !data.has_more; + } + + loadLogs(); });