fixes
This commit is contained in:
56
app.py
56
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,7 +66,6 @@ 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')
|
||||
@@ -88,41 +84,70 @@ try:
|
||||
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=[],
|
||||
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)
|
||||
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=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():
|
||||
@@ -134,6 +159,5 @@ def home():
|
||||
layer7_count=layer7_count,
|
||||
layer4_count=layer4_count)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='::', port=5000, ssl_context=ssl_context, debug=True)
|
||||
@@ -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 = `<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();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user