new_functions #2

Merged
gru merged 8 commits from new_functions into master 2025-11-04 09:04:37 +01:00
2 changed files with 153 additions and 125 deletions
Showing only changes of commit 27f9984574 - Show all commits

54
app.py
View File

@@ -1,10 +1,8 @@
import os import os
import sys import sys
import ssl import ssl
import configparser 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.main_routes import main_bp
from routes.edit_routes import edit_bp from routes.edit_routes import edit_bp
from utils.stats_utils import fetch_haproxy_stats, parse_haproxy_stats 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(main_bp)
app.register_blueprint(edit_bp) app.register_blueprint(edit_bp)
setup_auth(app) setup_auth(app)
certificate_path = None certificate_path = None
@@ -69,7 +66,6 @@ ssl_context = None
try: try:
config2 = configparser.ConfigParser() config2 = configparser.ConfigParser()
config2.read(SSL_INI) config2.read(SSL_INI)
if config2.has_section('ssl'): if config2.has_section('ssl'):
certificate_path = config2.get('ssl', 'certificate_path') certificate_path = config2.get('ssl', 'certificate_path')
private_key_path = config2.get('ssl', 'private_key_path') private_key_path = config2.get('ssl', 'private_key_path')
@@ -88,41 +84,70 @@ try:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
ssl_context.load_cert_chain(certfile=certificate_path, keyfile=private_key_path) ssl_context.load_cert_chain(certfile=certificate_path, keyfile=private_key_path)
print(f"[APP] SSL context loaded", flush=True) print(f"[APP] SSL context loaded", flush=True)
except Exception as e: except Exception as e:
print(f"[APP] SSL error: {e}", flush=True) print(f"[APP] SSL error: {e}", flush=True)
sys.exit(1) sys.exit(1)
@app.route('/statistics') @app.route('/statistics')
def display_haproxy_stats(): def display_haproxy_stats():
haproxy_stats = fetch_haproxy_stats() haproxy_stats = fetch_haproxy_stats()
parsed_stats = parse_haproxy_stats(haproxy_stats) parsed_stats = parse_haproxy_stats(haproxy_stats)
return render_template('statistics.html', stats=parsed_stats) return render_template('statistics.html', stats=parsed_stats)
@app.route('/logs', endpoint='display_logs') @app.route('/logs', endpoint='display_logs')
#@requires_auth
def display_haproxy_logs(): def display_haproxy_logs():
log_file_path = '/var/log/haproxy.log' log_file_path = '/var/log/haproxy.log'
if not os.path.exists(log_file_path): if not os.path.exists(log_file_path):
return render_template('logs.html', return render_template('logs.html',
logs=[], logs=[],
total_logs=0,
error_message=f"Log file not found: {log_file_path}") error_message=f"Log file not found: {log_file_path}")
try: try:
logs = parse_log_file(log_file_path) logs = parse_log_file(log_file_path)
if not logs: 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', return render_template('logs.html',
logs=[], logs=initial_logs,
error_message="Log file is empty or unreadable") total_logs=total_logs,
return render_template('logs.html', logs=logs) loaded_count=len(initial_logs))
except Exception as e: except Exception as e:
return render_template('logs.html', return render_template('logs.html',
logs=[], logs=[],
total_logs=0,
error_message=f"Error parsing logs: {str(e)}") 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') @app.route('/home')
def home(): def home():
@@ -134,6 +159,5 @@ def home():
layer7_count=layer7_count, layer7_count=layer7_count,
layer4_count=layer4_count) layer4_count=layer4_count)
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='::', port=5000, ssl_context=ssl_context, debug=True) app.run(host='::', port=5000, ssl_context=ssl_context, debug=True)

View File

@@ -1,103 +1,107 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const filterIp = document.getElementById('filter_ip'); let currentPage = 1;
const filterStatus = document.getElementById('filter_status'); let perPage = 50;
const filterMethod = document.getElementById('filter_method'); let totalLogs = parseInt(document.getElementById('total_count').textContent);
const filterThreats = document.getElementById('filter_threats'); let allLogs = [];
const filterHideStats = document.getElementById('filter_hide_stats');
const resetBtn = document.getElementById('reset_filters');
const logsTable = document.getElementById('logs_table'); const logsContainer = document.getElementById('logs_container');
if (!logsTable) return; // Exit if no logs 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')); perPageSelect.addEventListener('change', function(e) {
perPage = parseInt(e.target.value);
currentPage = 1;
loadLogs();
});
// Filter function refreshBtn.addEventListener('click', function() {
function applyFilters() { currentPage = 1;
const ipValue = filterIp.value.toLowerCase(); loadLogs();
const statusValue = filterStatus.value; });
const methodValue = filterMethod.value;
const showThreats = filterThreats.checked;
const hideStats = filterHideStats.checked;
let visibleCount = 0; prevBtn.addEventListener('click', function() {
let threatCount = 0; if (currentPage > 1) {
let count2xx = 0, count4xx = 0, count5xx = 0; currentPage--;
const uniqueIps = new Set(); loadLogs();
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 nextBtn.addEventListener('click', function() {
document.getElementById('stat_total').textContent = visibleCount; const totalPages = Math.ceil(totalLogs / perPage);
document.getElementById('stat_threats').textContent = threatCount; if (currentPage < totalPages) {
document.getElementById('stat_2xx').textContent = count2xx; currentPage++;
document.getElementById('stat_4xx').textContent = count4xx; loadLogs();
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();
}); });
applyFilters(); 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 = `<tr><td class="alert alert-danger">Błąd załadowania logów</td></tr>`;
});
}
function renderLogs(logs) {
if (!logs || logs.length === 0) {
logsContainer.innerHTML = '<tr><td class="text-center text-muted py-4"><i class="bi bi-inbox"></i> Brak logów</td></tr>';
return;
}
logsContainer.innerHTML = logs.map(entry => `
<tr>
<td>
<small style="font-family: monospace; color: #666;">
<i class="bi bi-clock text-muted me-1"></i>${entry.timestamp || 'N/A'}<br>
<span class="text-muted">${entry.source || 'N/A'}</span><br>
<code style="color: #333; word-break: break-all; display: block; margin-top: 4px;">
${entry.message || 'N/A'}
</code>
</small>
</td>
</tr>
`).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();
}); });