diff --git a/src/database.py b/src/database.py
index 0e5b40b..ffd2592 100644
--- a/src/database.py
+++ b/src/database.py
@@ -1755,14 +1755,19 @@ class DatabaseManager:
offset = (page - 1) * page_size
results = (
- session.query(AccessLog.ip, func.count(AccessLog.id).label("count"))
- .group_by(AccessLog.ip)
+ session.query(
+ AccessLog.ip,
+ func.count(AccessLog.id).label("count"),
+ IpStats.category,
+ )
+ .outerjoin(IpStats, AccessLog.ip == IpStats.ip)
+ .group_by(AccessLog.ip, IpStats.category)
.all()
)
# Filter out local/private IPs and server IP, then sort
filtered = [
- {"ip": row.ip, "count": row.count}
+ {"ip": row.ip, "count": row.count, "category": row.category or "unknown"}
for row in results
if is_valid_public_ip(row.ip, server_ip)
]
diff --git a/src/templates/jinja2/dashboard/index.html b/src/templates/jinja2/dashboard/index.html
index a0dbf8c..d4cb7d3 100644
--- a/src/templates/jinja2/dashboard/index.html
+++ b/src/templates/jinja2/dashboard/index.html
@@ -41,21 +41,10 @@
{# ==================== OVERVIEW TAB ==================== #}
-
+
- {# Suspicious Activity - server-rendered #}
- {% include "dashboard/partials/suspicious_table.html" %}
-
- {# Honeypot Triggers - HTMX loaded #}
-
-
Honeypot Triggers by IP
-
-
+ {# Map section #}
+ {% include "dashboard/partials/map_section.html" %}
{# Top IPs + Top User-Agents side by side #}
@@ -89,14 +78,14 @@
Loading...
+
+ {# Suspicious Activity - server-rendered #}
+ {% include "dashboard/partials/suspicious_table.html" %}
{# ==================== ATTACKS TAB ==================== #}
- {# Map section #}
- {% include "dashboard/partials/map_section.html" %}
-
{# Attackers table - HTMX loaded #}
Attackers by Total Requests
@@ -119,6 +108,17 @@
+ {# Honeypot Triggers - HTMX loaded #}
+
+
Honeypot Triggers by IP
+
+
+
{# Attack Types table #}
Detected Attack Types
diff --git a/src/templates/jinja2/dashboard/partials/access_by_ip_table.html b/src/templates/jinja2/dashboard/partials/access_by_ip_table.html
index 34306bc..5e7bd6c 100644
--- a/src/templates/jinja2/dashboard/partials/access_by_ip_table.html
+++ b/src/templates/jinja2/dashboard/partials/access_by_ip_table.html
@@ -26,7 +26,7 @@
hx-swap="innerHTML">
Time
-
Actions |
+
|
@@ -50,14 +50,14 @@
- |
+ |
|
{% else %}
- | No logs detected |
+ | No logs detected |
{% endfor %}
diff --git a/src/templates/jinja2/dashboard/partials/attack_types_table.html b/src/templates/jinja2/dashboard/partials/attack_types_table.html
index 9d8bb30..9a2451a 100644
--- a/src/templates/jinja2/dashboard/partials/attack_types_table.html
+++ b/src/templates/jinja2/dashboard/partials/attack_types_table.html
@@ -28,7 +28,7 @@
hx-swap="innerHTML">
Time
-
Actions |
+
|
diff --git a/src/templates/jinja2/dashboard/partials/attackers_table.html b/src/templates/jinja2/dashboard/partials/attackers_table.html
index 4e8a987..23c61e8 100644
--- a/src/templates/jinja2/dashboard/partials/attackers_table.html
+++ b/src/templates/jinja2/dashboard/partials/attackers_table.html
@@ -28,7 +28,7 @@
First Seen |
Last Seen |
Location |
- Actions |
+ |
diff --git a/src/templates/jinja2/dashboard/partials/credentials_table.html b/src/templates/jinja2/dashboard/partials/credentials_table.html
index 92af527..491b5b2 100644
--- a/src/templates/jinja2/dashboard/partials/credentials_table.html
+++ b/src/templates/jinja2/dashboard/partials/credentials_table.html
@@ -28,7 +28,7 @@
hx-swap="innerHTML">
Time
- Actions |
+ |
diff --git a/src/templates/jinja2/dashboard/partials/honeypot_table.html b/src/templates/jinja2/dashboard/partials/honeypot_table.html
index f7cd1da..3215bc7 100644
--- a/src/templates/jinja2/dashboard/partials/honeypot_table.html
+++ b/src/templates/jinja2/dashboard/partials/honeypot_table.html
@@ -25,7 +25,7 @@
hx-swap="innerHTML">
Honeypot Triggers
- Actions |
+ |
diff --git a/src/templates/jinja2/dashboard/partials/suspicious_table.html b/src/templates/jinja2/dashboard/partials/suspicious_table.html
index 172e6c8..c10502c 100644
--- a/src/templates/jinja2/dashboard/partials/suspicious_table.html
+++ b/src/templates/jinja2/dashboard/partials/suspicious_table.html
@@ -8,7 +8,7 @@
Path |
User-Agent |
Time |
- Actions |
+ |
diff --git a/src/templates/jinja2/dashboard/partials/top_ips_table.html b/src/templates/jinja2/dashboard/partials/top_ips_table.html
index d014668..afcf26b 100644
--- a/src/templates/jinja2/dashboard/partials/top_ips_table.html
+++ b/src/templates/jinja2/dashboard/partials/top_ips_table.html
@@ -19,13 +19,14 @@
| # |
IP Address |
+ Category |
Access Count
|
- Actions |
+ |
@@ -39,6 +40,11 @@
@click="toggleIpDetail($event)">
{{ item.ip | e }}
+
+ {% set cat = item.category | default('unknown') %}
+ {% set cat_colors = {'attacker': '#f85149', 'good_crawler': '#3fb950', 'bad_crawler': '#f0883e', 'regular_user': '#58a6ff', 'unknown': '#8b949e'} %}
+
+ |
{{ item.count }} |
|
- |
+ |
|
{% else %}
- | No data |
+ | No data |
{% endfor %}
diff --git a/src/templates/static/js/dashboard.js b/src/templates/static/js/dashboard.js
index eec56ca..e6e848b 100644
--- a/src/templates/static/js/dashboard.js
+++ b/src/templates/static/js/dashboard.js
@@ -45,15 +45,9 @@ document.addEventListener('alpine:init', () => {
this.tab = 'attacks';
window.location.hash = '#ip-stats';
- // Delay initialization to ensure the container is visible and
- // the browser has reflowed after x-show removes display:none.
- // Leaflet and Chart.js need visible containers with real dimensions.
+ // Delay chart initialization to ensure the container is visible
this.$nextTick(() => {
setTimeout(() => {
- if (!this.mapInitialized && typeof initializeAttackerMap === 'function') {
- initializeAttackerMap();
- this.mapInitialized = true;
- }
if (!this.chartLoaded && typeof loadAttackTypesChart === 'function') {
loadAttackTypesChart();
this.chartLoaded = true;