mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-11 13:47:05 +02:00
Implement multiline log pharsing support
This commit is contained in:
@@ -75,6 +75,14 @@ function openJailConfigModal(jailName) {
|
|||||||
// Check if logpath is set in jail config and show test button
|
// Check if logpath is set in jail config and show test button
|
||||||
updateLogpathButtonVisibility();
|
updateLogpathButtonVisibility();
|
||||||
|
|
||||||
|
// Show hint for local servers
|
||||||
|
var localServerHint = document.getElementById('localServerLogpathHint');
|
||||||
|
if (localServerHint && currentServer && currentServer.type === 'local') {
|
||||||
|
localServerHint.classList.remove('hidden');
|
||||||
|
} else if (localServerHint) {
|
||||||
|
localServerHint.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
// Add listener to update button visibility when jail config changes
|
// Add listener to update button visibility when jail config changes
|
||||||
jailTextArea.addEventListener('input', updateLogpathButtonVisibility);
|
jailTextArea.addEventListener('input', updateLogpathButtonVisibility);
|
||||||
|
|
||||||
@@ -102,11 +110,22 @@ function updateLogpathButtonVisibility() {
|
|||||||
var jailConfig = jailTextArea ? jailTextArea.value : '';
|
var jailConfig = jailTextArea ? jailTextArea.value : '';
|
||||||
var hasLogpath = /logpath\s*=/i.test(jailConfig);
|
var hasLogpath = /logpath\s*=/i.test(jailConfig);
|
||||||
var testSection = document.getElementById('testLogpathSection');
|
var testSection = document.getElementById('testLogpathSection');
|
||||||
|
var localServerHint = document.getElementById('localServerLogpathHint');
|
||||||
|
|
||||||
if (hasLogpath && testSection) {
|
if (hasLogpath && testSection) {
|
||||||
testSection.classList.remove('hidden');
|
testSection.classList.remove('hidden');
|
||||||
|
// Show hint for local servers
|
||||||
|
if (localServerHint && currentServer && currentServer.type === 'local') {
|
||||||
|
localServerHint.classList.remove('hidden');
|
||||||
|
} else if (localServerHint) {
|
||||||
|
localServerHint.classList.add('hidden');
|
||||||
|
}
|
||||||
} else if (testSection) {
|
} else if (testSection) {
|
||||||
testSection.classList.add('hidden');
|
testSection.classList.add('hidden');
|
||||||
document.getElementById('logpathResults').classList.add('hidden');
|
document.getElementById('logpathResults').classList.add('hidden');
|
||||||
|
if (localServerHint) {
|
||||||
|
localServerHint.classList.add('hidden');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,19 +168,68 @@ function saveJailConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract logpath from jail config text
|
// Extract logpath from jail config text
|
||||||
|
// Supports multiple logpaths in a single line (space-separated) or multiple lines
|
||||||
|
// Fail2ban supports both formats:
|
||||||
|
// logpath = /var/log/file1.log /var/log/file2.log
|
||||||
|
// logpath = /var/log/file1.log
|
||||||
|
// /var/log/file2.log
|
||||||
function extractLogpathFromConfig(configText) {
|
function extractLogpathFromConfig(configText) {
|
||||||
if (!configText) return '';
|
if (!configText) return '';
|
||||||
|
|
||||||
// Match logpath = value (handles various formats)
|
var logpaths = [];
|
||||||
var logpathMatch = configText.match(/^logpath\s*=\s*(.+)$/im);
|
var lines = configText.split('\n');
|
||||||
if (logpathMatch && logpathMatch[1]) {
|
var inLogpathLine = false;
|
||||||
// Trim whitespace and remove quotes if present
|
var currentLogpath = '';
|
||||||
var logpath = logpathMatch[1].trim();
|
|
||||||
// Remove surrounding quotes
|
for (var i = 0; i < lines.length; i++) {
|
||||||
logpath = logpath.replace(/^["']|["']$/g, '');
|
var line = lines[i].trim();
|
||||||
return logpath;
|
// Skip comments
|
||||||
|
if (line.startsWith('#')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this line starts with logpath =
|
||||||
|
var logpathMatch = line.match(/^logpath\s*=\s*(.+)$/i);
|
||||||
|
if (logpathMatch && logpathMatch[1]) {
|
||||||
|
// Trim whitespace and remove quotes if present
|
||||||
|
currentLogpath = logpathMatch[1].trim();
|
||||||
|
currentLogpath = currentLogpath.replace(/^["']|["']$/g, '');
|
||||||
|
inLogpathLine = true;
|
||||||
|
} else if (inLogpathLine) {
|
||||||
|
// Continuation line (indented or starting with space)
|
||||||
|
// Fail2ban allows continuation lines for logpath
|
||||||
|
if (line !== '' && !line.includes('=')) {
|
||||||
|
// This is a continuation line, append to current logpath
|
||||||
|
currentLogpath += ' ' + line.trim();
|
||||||
|
} else {
|
||||||
|
// End of logpath block, process current logpath
|
||||||
|
if (currentLogpath) {
|
||||||
|
// Split by spaces to handle multiple logpaths in one line
|
||||||
|
var paths = currentLogpath.split(/\s+/).filter(function(p) { return p.length > 0; });
|
||||||
|
logpaths = logpaths.concat(paths);
|
||||||
|
currentLogpath = '';
|
||||||
|
}
|
||||||
|
inLogpathLine = false;
|
||||||
|
}
|
||||||
|
} else if (inLogpathLine && line === '') {
|
||||||
|
// Empty line might end the logpath block
|
||||||
|
if (currentLogpath) {
|
||||||
|
var paths = currentLogpath.split(/\s+/).filter(function(p) { return p.length > 0; });
|
||||||
|
logpaths = logpaths.concat(paths);
|
||||||
|
currentLogpath = '';
|
||||||
|
}
|
||||||
|
inLogpathLine = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return '';
|
|
||||||
|
// Process any remaining logpath
|
||||||
|
if (currentLogpath) {
|
||||||
|
var paths = currentLogpath.split(/\s+/).filter(function(p) { return p.length > 0; });
|
||||||
|
logpaths = logpaths.concat(paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join multiple logpaths with newlines
|
||||||
|
return logpaths.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function testLogpath() {
|
function testLogpath() {
|
||||||
@@ -203,39 +271,88 @@ function testLogpath() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var originalLogpath = data.original_logpath || '';
|
var originalLogpath = data.original_logpath || '';
|
||||||
var resolvedLogpath = data.resolved_logpath || '';
|
var results = data.results || [];
|
||||||
var files = data.files || [];
|
var isLocalServer = data.is_local_server || false;
|
||||||
|
|
||||||
// Build output message with better formatting
|
// Build HTML output with visual indicators
|
||||||
var output = '';
|
var output = '';
|
||||||
|
|
||||||
// Show original logpath
|
if (results.length === 0) {
|
||||||
if (originalLogpath) {
|
output = '<div class="text-yellow-600">No logpath entries found.</div>';
|
||||||
output += 'Logpath:\n ' + originalLogpath + '\n\n';
|
resultsDiv.innerHTML = output;
|
||||||
|
resultsDiv.classList.add('text-yellow-600');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show resolved logpath if different from original
|
// Process each logpath result
|
||||||
if (resolvedLogpath && resolvedLogpath !== originalLogpath) {
|
results.forEach(function(result, idx) {
|
||||||
output += 'Resolved logpath:\n ' + resolvedLogpath + '\n\n';
|
var logpath = result.logpath || '';
|
||||||
} else if (resolvedLogpath) {
|
var resolvedPath = result.resolved_path || '';
|
||||||
//output += 'Logpath:\n ' + resolvedLogpath + '\n\n';
|
var found = result.found || false;
|
||||||
}
|
var files = result.files || [];
|
||||||
|
var error = result.error || '';
|
||||||
|
|
||||||
|
if (idx > 0) {
|
||||||
|
output += '<div class="my-4 border-t border-gray-300 pt-4"></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
output += '<div class="mb-3">';
|
||||||
|
output += '<div class="font-semibold text-gray-800 mb-1">Logpath ' + (idx + 1) + ':</div>';
|
||||||
|
output += '<div class="ml-4 text-sm text-gray-600 font-mono">' + escapeHtml(logpath) + '</div>';
|
||||||
|
|
||||||
|
if (resolvedPath && resolvedPath !== logpath) {
|
||||||
|
output += '<div class="ml-4 text-xs text-gray-500 mt-1">Resolved: <span class="font-mono">' + escapeHtml(resolvedPath) + '</span></div>';
|
||||||
|
}
|
||||||
|
output += '</div>';
|
||||||
|
|
||||||
|
// Test results
|
||||||
|
output += '<div class="ml-4 mb-2">';
|
||||||
|
output += '<div class="flex items-center gap-2">';
|
||||||
|
if (isLocalServer) {
|
||||||
|
output += '<span class="font-medium text-sm">In fail2ban-ui Container:</span>';
|
||||||
|
} else {
|
||||||
|
output += '<span class="font-medium text-sm">On Remote Server:</span>';
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
output += '<span class="text-red-600 font-bold">✗</span>';
|
||||||
|
output += '<span class="text-red-600 text-sm">Error: ' + escapeHtml(error) + '</span>';
|
||||||
|
} else if (found) {
|
||||||
|
output += '<span class="text-green-600 font-bold">✓</span>';
|
||||||
|
output += '<span class="text-green-600 text-sm">Found ' + files.length + ' file(s)</span>';
|
||||||
|
} else {
|
||||||
|
output += '<span class="text-red-600 font-bold">✗</span>';
|
||||||
|
if (isLocalServer) {
|
||||||
|
output += '<span class="text-red-600 text-sm">Not found (logs may not be mounted to container)</span>';
|
||||||
|
} else {
|
||||||
|
output += '<span class="text-red-600 text-sm">Not found</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output += '</div>';
|
||||||
|
if (files.length > 0) {
|
||||||
|
output += '<div class="ml-6 mt-1 text-xs text-gray-600">';
|
||||||
|
files.forEach(function(file) {
|
||||||
|
output += '<div class="font-mono"> • ' + escapeHtml(file) + '</div>';
|
||||||
|
});
|
||||||
|
output += '</div>';
|
||||||
|
}
|
||||||
|
output += '</div>';
|
||||||
|
});
|
||||||
|
|
||||||
// Show files found with better formatting
|
// Set overall status color
|
||||||
if (files.length === 0) {
|
var allFound = results.every(function(r) { return r.found; });
|
||||||
output += 'No files found matching the logpath pattern.';
|
var anyFound = results.some(function(r) { return r.found; });
|
||||||
|
|
||||||
|
if (allFound) {
|
||||||
|
resultsDiv.classList.remove('text-red-600', 'text-yellow-600');
|
||||||
|
} else if (anyFound) {
|
||||||
resultsDiv.classList.remove('text-red-600');
|
resultsDiv.classList.remove('text-red-600');
|
||||||
resultsDiv.classList.add('text-yellow-600');
|
resultsDiv.classList.add('text-yellow-600');
|
||||||
} else {
|
} else {
|
||||||
output += 'Found ' + files.length + ' file(s):\n\n';
|
resultsDiv.classList.remove('text-yellow-600');
|
||||||
files.forEach(function(file, index) {
|
resultsDiv.classList.add('text-red-600');
|
||||||
output += ' ' + (index + 1) + '. ' + file + '\n';
|
|
||||||
});
|
|
||||||
resultsDiv.classList.remove('text-red-600', 'text-yellow-600');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use textContent for plain text, but we could also use innerHTML for better formatting
|
resultsDiv.innerHTML = output;
|
||||||
resultsDiv.textContent = output;
|
|
||||||
|
|
||||||
// Auto-scroll to results
|
// Auto-scroll to results
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
|
|||||||
@@ -1028,11 +1028,14 @@
|
|||||||
|
|
||||||
<!-- Test Logpath Button (only shown if logpath is set) -->
|
<!-- Test Logpath Button (only shown if logpath is set) -->
|
||||||
<div id="testLogpathSection" class="hidden">
|
<div id="testLogpathSection" class="hidden">
|
||||||
|
<div id="localServerLogpathHint" class="mb-2 p-2 bg-blue-50 border border-blue-200 rounded-md text-xs text-blue-800 hidden">
|
||||||
|
<strong data-i18n="modal.local_server_logpath_note">ℹ️ Note:</strong> <span data-i18n="modal.local_server_logpath_text_prefix">For a local fail2ban server (e.g. installed on container host system or in a container on same host), log files must also be mounted to the fail2ban-ui container (e.g.,</span> <code class="font-mono">-v /var/log:/var/log:ro</code> <span data-i18n="modal.local_server_logpath_text_suffix">) this is required so that the fail2ban-ui can verify logpath variables or paths when updating jails.</span>
|
||||||
|
</div>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-600 text-sm font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
|
class="inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-600 text-sm font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
|
||||||
onclick="testLogpath()"
|
onclick="testLogpath()"
|
||||||
data-i18n="modal.test_logpath">Test Logpath</button>
|
data-i18n="modal.test_logpath">Test Logpath</button>
|
||||||
<pre id="logpathResults" class="mt-2 p-3 bg-gray-100 rounded-md text-sm font-mono max-h-32 overflow-y-auto hidden"></pre>
|
<div id="logpathResults" class="mt-2 p-3 bg-gray-100 rounded-md text-sm max-h-64 overflow-y-auto hidden"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user