diff --git a/src/templates/dashboard_template.py b/src/templates/dashboard_template.py index 89ca4fb..b8db40c 100644 --- a/src/templates/dashboard_template.py +++ b/src/templates/dashboard_template.py @@ -9,6 +9,11 @@ import html from datetime import datetime from zoneinfo import ZoneInfo +# imports for the __init_subclass__ method, do not remove pls +from firewall import fwtype +from firewall.iptables import Iptables +from firewall.raw import Raw + def _escape(value) -> str: """Escape HTML special characters to prevent XSS attacks.""" @@ -653,11 +658,13 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: BlessedRebuS/Krawl -
- - Export Malicious IPs - -
+
+ + +

Krawl Dashboard

@@ -1106,7 +1113,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: if (stats.category_history && stats.category_history.length > 0) {{ html += '
'; html += '
'; - + // Timeline column html += '
'; html += '
Behavior Timeline
'; @@ -1117,18 +1124,18 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: const timestamp = formatTimestamp(change.timestamp); const oldClass = change.old_category ? 'category-' + change.old_category.toLowerCase().replace('_', '-') : ''; const newClass = 'category-' + categoryClass; - + html += '
'; html += `
`; html += '
'; - + if (change.old_category) {{ html += `${{change.old_category}}`; html += ''; }} else {{ html += 'Initial:'; }} - + html += `${{change.new_category}}`; html += `
${{timestamp}}
`; html += '
'; @@ -1137,14 +1144,14 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: html += '
'; html += '
'; - + // Reputation column html += '
'; - + if (stats.list_on && Object.keys(stats.list_on).length > 0) {{ html += '
Listed On
'; const sortedSources = Object.entries(stats.list_on).sort((a, b) => a[0].localeCompare(b[0])); - + sortedSources.forEach(([source, url]) => {{ if (url && url !== 'N/A') {{ html += `${{source}}`; @@ -1156,7 +1163,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: html += '
Reputation
'; html += '✓ Clean'; }} - + html += '
'; html += '
'; html += '
'; @@ -1360,23 +1367,23 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: document.querySelectorAll('.tab-content').forEach(tab => {{ tab.classList.remove('active'); }}); - + // Remove active class from all buttons document.querySelectorAll('.tab-button').forEach(btn => {{ btn.classList.remove('active'); }}); - + // Show selected tab const selectedTab = document.getElementById(tabName); const selectedButton = document.querySelector(`.tab-button[href="#${{tabName}}"]`); - + if (selectedTab) {{ selectedTab.classList.add('active'); }} if (selectedButton) {{ selectedButton.classList.add('active'); }} - + // Load data for this tab if (tabName === 'ip-stats') {{ loadIpStatistics(1); @@ -1418,7 +1425,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: if (e.target.classList.contains('sortable') && e.target.closest('#ip-stats-tbody')) {{ return; // Don't sort when inside tbody }} - + const sortHeader = e.target.closest('th.sortable'); if (!sortHeader) return; @@ -1426,7 +1433,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: if (!table || !table.classList.contains('ip-stats-table')) return; const sortField = sortHeader.getAttribute('data-sort'); - + // Toggle sort order if clicking the same field if (currentSortBy === sortField) {{ currentSortOrder = currentSortOrder === 'desc' ? 'asc' : 'desc'; @@ -1457,9 +1464,9 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: console.error('IP stats tbody not found'); return; }} - + tbody.innerHTML = 'Loading...'; - + try {{ console.log('Fetching attackers from page:', page, 'sort:', currentSortBy, currentSortOrder); const response = await fetch(DASHBOARD_PATH + '/api/attackers?page=' + page + '&page_size=' + PAGE_SIZE + '&sort_by=' + currentSortBy + '&sort_order=' + currentSortOrder, {{ @@ -1469,14 +1476,14 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: 'Pragma': 'no-cache' }} }}); - + console.log('Response status:', response.status); - + if (!response.ok) throw new Error(`HTTP ${{response.status}}`); - + const data = await response.json(); console.log('Received data:', data); - + if (!data.attackers || data.attackers.length === 0) {{ tbody.innerHTML = 'No attackers on this page.'; currentPage = page; @@ -1484,7 +1491,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: updatePaginationControls(); return; }} - + // Update pagination info currentPage = data.pagination.page; totalPages = data.pagination.total_pages; @@ -1492,7 +1499,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: document.getElementById('total-pages').textContent = totalPages; document.getElementById('total-attackers').textContent = data.pagination.total_attackers; updatePaginationControls(); - + let html = ''; data.attackers.forEach((attacker, index) => {{ const rank = (currentPage - 1) * PAGE_SIZE + index + 1; @@ -1512,10 +1519,10 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: `; }}); - + tbody.innerHTML = html; console.log('Populated', data.attackers.length, 'attacker records'); - + // Re-attach click listeners for expandable rows attachAttackerClickListeners(); }} catch (err) {{ @@ -1527,7 +1534,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: function updatePaginationControls() {{ const prevBtn = document.getElementById('prev-page-btn'); const nextBtn = document.getElementById('next-page-btn'); - + if (prevBtn) prevBtn.disabled = currentPage <= 1; if (nextBtn) nextBtn.disabled = currentPage >= totalPages; }} @@ -1799,7 +1806,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: async function loadOverviewTable(tableId) {{ const config = tableConfig[tableId]; if (!config) return; - + const state = overviewState[tableId]; const tbody = document.getElementById(tableId + '-tbody'); if (!tbody) return; @@ -1833,7 +1840,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: let html = ''; items.forEach((item, index) => {{ const rank = (state.currentPage - 1) * 5 + index + 1; - + if (tableId === 'honeypot') {{ html += `${{rank}}${{item.ip}}${{item.paths.join(', ')}}${{item.count}}`; html += ` @@ -1979,12 +1986,12 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: async function showIpDetail(ip) {{ const modal = document.getElementById('ip-detail-modal'); const bodyDiv = document.getElementById('ip-detail-body'); - + if (!modal || !bodyDiv) return; - + bodyDiv.innerHTML = '
Loading IP details...
'; modal.classList.add('show'); - + try {{ const response = await fetch(`${{DASHBOARD_PATH}}/api/ip-stats/${{ip}}`, {{ cache: 'no-store', @@ -1993,9 +2000,9 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: 'Pragma': 'no-cache' }} }}); - + if (!response.ok) throw new Error(`HTTP ${{response.status}}`); - + const stats = await response.json(); bodyDiv.innerHTML = '

' + stats.ip + ' - Detailed Statistics

' + formatIpStats(stats); }} catch (err) {{ @@ -2422,7 +2429,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: // Initialize map when Attacks tab is opened const originalSwitchTab = window.switchTab; let attackTypesChartLoaded = false; - + window.switchTab = function(tabName) {{ originalSwitchTab(tabName); if (tabName === 'ip-stats') {{ @@ -2455,7 +2462,7 @@ def generate_dashboard(stats: dict, dashboard_path: str = "") -> str: }}); if (!response.ok) throw new Error('Failed to fetch attack types'); - + const data = await response.json(); const attacks = data.attacks || [];