Add basic whois and logs modal to view stored events from local db

This commit is contained in:
2025-11-17 10:29:48 +01:00
parent 2dd62b63e9
commit b261a2e92e
7 changed files with 190 additions and 7 deletions

View File

@@ -47,6 +47,12 @@
"logs.table.ip": "IP", "logs.table.ip": "IP",
"logs.table.time": "Zeit", "logs.table.time": "Zeit",
"logs.table.country": "Land", "logs.table.country": "Land",
"logs.table.actions": "Aktionen",
"logs.actions.whois": "Whois",
"logs.actions.logs": "Logs",
"logs.modal.whois_title": "Whois-Informationen",
"logs.modal.logs_title": "Logs",
"logs.modal.jail": "Jail",
"filter_debug.title": "Filter-Debug", "filter_debug.title": "Filter-Debug",
"filter_debug.select_filter": "Wählen Sie einen Filter", "filter_debug.select_filter": "Wählen Sie einen Filter",
"filter_debug.log_lines": "Logzeilen", "filter_debug.log_lines": "Logzeilen",

View File

@@ -47,6 +47,12 @@
"logs.table.ip": "IP", "logs.table.ip": "IP",
"logs.table.time": "Zyt", "logs.table.time": "Zyt",
"logs.table.country": "Land", "logs.table.country": "Land",
"logs.table.actions": "Aktione",
"logs.actions.whois": "Whois",
"logs.actions.logs": "Logs",
"logs.modal.whois_title": "Whois-Informatione",
"logs.modal.logs_title": "Logs",
"logs.modal.jail": "Jail",
"filter_debug.title": "Filter Debug", "filter_debug.title": "Filter Debug",
"filter_debug.select_filter": "Wähl en Filter us", "filter_debug.select_filter": "Wähl en Filter us",
"filter_debug.log_lines": "Log-Zile", "filter_debug.log_lines": "Log-Zile",

View File

@@ -47,6 +47,12 @@
"logs.table.ip": "IP", "logs.table.ip": "IP",
"logs.table.time": "Time", "logs.table.time": "Time",
"logs.table.country": "Country", "logs.table.country": "Country",
"logs.table.actions": "Actions",
"logs.actions.whois": "Whois",
"logs.actions.logs": "Logs",
"logs.modal.whois_title": "Whois Information",
"logs.modal.logs_title": "Logs",
"logs.modal.jail": "Jail",
"filter_debug.title": "Filter Debug", "filter_debug.title": "Filter Debug",
"filter_debug.select_filter": "Select a Filter", "filter_debug.select_filter": "Select a Filter",
"filter_debug.log_lines": "Log Lines", "filter_debug.log_lines": "Log Lines",

View File

@@ -45,8 +45,14 @@
"logs.table.count": "Cantidad", "logs.table.count": "Cantidad",
"logs.table.jail": "Jail", "logs.table.jail": "Jail",
"logs.table.ip": "IP", "logs.table.ip": "IP",
"logs.table.time": "Hora", "logs.table.time": "Hora",
"logs.table.country": "País", "logs.table.country": "País",
"logs.table.actions": "Acciones",
"logs.actions.whois": "Whois",
"logs.actions.logs": "Registros",
"logs.modal.whois_title": "Información Whois",
"logs.modal.logs_title": "Registros",
"logs.modal.jail": "Jail",
"filter_debug.title": "Depuración de filtros", "filter_debug.title": "Depuración de filtros",
"filter_debug.select_filter": "Selecciona un filtro", "filter_debug.select_filter": "Selecciona un filtro",
"filter_debug.log_lines": "Líneas de log", "filter_debug.log_lines": "Líneas de log",

View File

@@ -45,8 +45,14 @@
"logs.table.count": "Nombre", "logs.table.count": "Nombre",
"logs.table.jail": "Jail", "logs.table.jail": "Jail",
"logs.table.ip": "IP", "logs.table.ip": "IP",
"logs.table.time": "Heure", "logs.table.time": "Heure",
"logs.table.country": "Pays", "logs.table.country": "Pays",
"logs.table.actions": "Actions",
"logs.actions.whois": "Whois",
"logs.actions.logs": "Journaux",
"logs.modal.whois_title": "Informations Whois",
"logs.modal.logs_title": "Journaux",
"logs.modal.jail": "Jail",
"filter_debug.title": "Débogage des filtres", "filter_debug.title": "Débogage des filtres",
"filter_debug.select_filter": "Sélectionnez un filtre", "filter_debug.select_filter": "Sélectionnez un filtre",
"filter_debug.log_lines": "Lignes de log", "filter_debug.log_lines": "Lignes de log",

View File

@@ -45,8 +45,14 @@
"logs.table.count": "Conteggio", "logs.table.count": "Conteggio",
"logs.table.jail": "Jail", "logs.table.jail": "Jail",
"logs.table.ip": "IP", "logs.table.ip": "IP",
"logs.table.time": "Ora", "logs.table.time": "Ora",
"logs.table.country": "Paese", "logs.table.country": "Paese",
"logs.table.actions": "Azioni",
"logs.actions.whois": "Whois",
"logs.actions.logs": "Log",
"logs.modal.whois_title": "Informazioni Whois",
"logs.modal.logs_title": "Log",
"logs.modal.jail": "Jail",
"filter_debug.title": "Debug Filtro", "filter_debug.title": "Debug Filtro",
"filter_debug.select_filter": "Seleziona un filtro", "filter_debug.select_filter": "Seleziona un filtro",
"filter_debug.log_lines": "Righe di log", "filter_debug.log_lines": "Righe di log",

View File

@@ -823,6 +823,68 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Whois Modal -->
<div id="whoisModal" class="hidden fixed inset-0 z-50 overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
<h3 class="text-lg leading-6 font-medium text-gray-900">
<span data-i18n="logs.modal.whois_title">Whois Information</span> - <span id="whoisModalIP"></span>
</h3>
<div class="mt-4">
<pre id="whoisModalContent" class="w-full border border-gray-300 rounded-md px-3 py-2 bg-gray-900 text-green-400 font-mono text-xs overflow-x-auto" style="max-height: 70vh; white-space: pre-wrap; word-wrap: break-word;"></pre>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" class="w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm" onclick="closeModal('whoisModal')" data-i18n="modal.close">Close</button>
</div>
</div>
</div>
</div>
<!-- Logs Modal -->
<div id="logsModal" class="hidden fixed inset-0 z-50 overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-2">
<span data-i18n="logs.modal.logs_title">Logs</span> - <span id="logsModalIP"></span>
</h3>
<p class="text-sm text-gray-600 mb-4">
<span data-i18n="logs.modal.jail">Jail:</span> <span id="logsModalJail" class="font-semibold"></span>
</p>
<div class="mt-4">
<pre id="logsModalContent" class="w-full border border-gray-300 rounded-md px-3 py-2 bg-gray-900 text-green-400 font-mono text-xs overflow-x-auto" style="max-height: 70vh; white-space: pre-wrap; word-wrap: break-word;"></pre>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" class="w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm" onclick="closeModal('logsModal')" data-i18n="modal.close">Close</button>
</div>
</div>
</div>
</div>
<!-- ********************** Modal Templates END ************************ --> <!-- ********************** Modal Templates END ************************ -->
<!-- jQuery (used by Select2) --> <!-- jQuery (used by Select2) -->
@@ -1470,10 +1532,13 @@
+ ' <th class="hidden sm:table-cell px-2 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="logs.table.jail">Jail</th>' + ' <th class="hidden sm:table-cell px-2 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="logs.table.jail">Jail</th>'
+ ' <th class="px-2 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="logs.table.ip">IP</th>' + ' <th class="px-2 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="logs.table.ip">IP</th>'
+ ' <th class="hidden md:table-cell px-2 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="logs.table.country">Country</th>' + ' <th class="hidden md:table-cell px-2 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="logs.table.country">Country</th>'
+ ' <th class="px-2 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" data-i18n="logs.table.actions">Actions</th>'
+ ' </tr>' + ' </tr>'
+ ' </thead>' + ' </thead>'
+ ' <tbody class="bg-white divide-y divide-gray-200">'; + ' <tbody class="bg-white divide-y divide-gray-200">';
latestBanEvents.forEach(function(event) { latestBanEvents.forEach(function(event, index) {
var hasWhois = event.whois && event.whois.trim().length > 0;
var hasLogs = event.logs && event.logs.trim().length > 0;
html += '' html += ''
+ ' <tr class="hover:bg-gray-50">' + ' <tr class="hover:bg-gray-50">'
+ ' <td class="px-2 py-2 whitespace-nowrap">' + escapeHtml(formatDateTime(event.occurredAt || event.createdAt)) + '</td>' + ' <td class="px-2 py-2 whitespace-nowrap">' + escapeHtml(formatDateTime(event.occurredAt || event.createdAt)) + '</td>'
@@ -1481,6 +1546,12 @@
+ ' <td class="hidden sm:table-cell px-2 py-2 whitespace-nowrap">' + escapeHtml(event.jail || '') + '</td>' + ' <td class="hidden sm:table-cell px-2 py-2 whitespace-nowrap">' + escapeHtml(event.jail || '') + '</td>'
+ ' <td class="px-2 py-2 whitespace-nowrap">' + escapeHtml(event.ip || '') + '</td>' + ' <td class="px-2 py-2 whitespace-nowrap">' + escapeHtml(event.ip || '') + '</td>'
+ ' <td class="hidden md:table-cell px-2 py-2 whitespace-nowrap">' + escapeHtml(event.country || '—') + '</td>' + ' <td class="hidden md:table-cell px-2 py-2 whitespace-nowrap">' + escapeHtml(event.country || '—') + '</td>'
+ ' <td class="px-2 py-2 whitespace-nowrap">'
+ ' <div class="flex gap-2">'
+ (hasWhois ? ' <button onclick="openWhoisModal(' + index + ')" class="px-2 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700" data-i18n="logs.actions.whois">Whois</button>' : ' <button disabled class="px-2 py-1 text-xs bg-gray-300 text-gray-500 rounded cursor-not-allowed" data-i18n="logs.actions.whois">Whois</button>')
+ (hasLogs ? ' <button onclick="openLogsModal(' + index + ')" class="px-2 py-1 text-xs bg-green-600 text-white rounded hover:bg-green-700" data-i18n="logs.actions.logs">Logs</button>' : ' <button disabled class="px-2 py-1 text-xs bg-gray-300 text-gray-500 rounded cursor-not-allowed" data-i18n="logs.actions.logs">Logs</button>')
+ ' </div>'
+ ' </td>'
+ ' </tr>'; + ' </tr>';
}); });
html += ' </tbody></table></div>'; html += ' </tbody></table></div>';
@@ -2052,6 +2123,82 @@
}); });
} }
// Function: openWhoisModal
// Opens the whois modal with data from the event at the given index
function openWhoisModal(eventIndex) {
if (!latestBanEvents || !latestBanEvents[eventIndex]) {
showToast("Event not found", 'error');
return;
}
var event = latestBanEvents[eventIndex];
if (!event.whois || !event.whois.trim()) {
showToast("No whois data available for this event", 'info');
return;
}
document.getElementById('whoisModalIP').textContent = event.ip || 'N/A';
var contentEl = document.getElementById('whoisModalContent');
contentEl.textContent = event.whois;
openModal('whoisModal');
}
// Function: openLogsModal
// Opens the logs modal with data from the event at the given index
// Highlights the line that caused the block
function openLogsModal(eventIndex) {
if (!latestBanEvents || !latestBanEvents[eventIndex]) {
showToast("Event not found", 'error');
return;
}
var event = latestBanEvents[eventIndex];
if (!event.logs || !event.logs.trim()) {
showToast("No logs data available for this event", 'info');
return;
}
document.getElementById('logsModalIP').textContent = event.ip || 'N/A';
document.getElementById('logsModalJail').textContent = event.jail || 'N/A';
var logs = event.logs;
var ip = event.ip || '';
var logLines = logs.split('\n');
// Find the line that likely caused the block
// Look for lines containing the IP address, prefer the last one
var highlightedLineIndex = -1;
for (var i = logLines.length - 1; i >= 0; i--) {
if (ip && logLines[i].indexOf(ip) !== -1) {
highlightedLineIndex = i;
break;
}
}
// If no line with IP found, highlight the last line
if (highlightedLineIndex === -1 && logLines.length > 0) {
highlightedLineIndex = logLines.length - 1;
}
// Build HTML with highlighted line
var contentEl = document.getElementById('logsModalContent');
if (highlightedLineIndex >= 0) {
var html = '';
for (var i = 0; i < logLines.length; i++) {
var line = escapeHtml(logLines[i] || '');
if (i === highlightedLineIndex) {
// Highlight the entire line - use inline span that covers the full width
html += '<span style="display: block; background-color: #d97706; color: #fef3c7; padding: 0.25rem 0.5rem; margin: 0.125rem 0; border-radius: 0.25rem;">' + line + '</span>';
} else {
html += line + '\n';
}
}
contentEl.innerHTML = html;
} else {
contentEl.textContent = logs;
}
openModal('logsModal');
}
// Function: openManageJailsModal // Function: openManageJailsModal
// Fetches the full-list of all jails (from /jails/manage) and builds a list with toggle switches. // Fetches the full-list of all jails (from /jails/manage) and builds a list with toggle switches.
function openManageJailsModal() { function openManageJailsModal() {