New Alert Profile
@@ -261,6 +265,49 @@
+
+
+
+
+
+
+
+ Ban IP / CIDR
+
+
+ Adds to block-cidrs-manual ConfigMap.
+ Separate multiple entries with commas or newlines.
+
+
+
+
+
+
+
+
diff --git a/nginxmon/urls.py b/nginxmon/urls.py
index 688b035..c17974e 100644
--- a/nginxmon/urls.py
+++ b/nginxmon/urls.py
@@ -20,6 +20,9 @@ urlpatterns = [
path('alert//dismiss/', views.DismissAlertView.as_view(), name='nginxmon-dismiss-alert'),
path('ban//unban/', views.UnbanIPView.as_view(), name='nginxmon-unban'),
+ # Manual IP ban form
+ path('actions/ip-ban/', views.IPBanView.as_view(), name='nginxmon-ip-ban'),
+
# HTMX partials & APIs
path('api/live-logs/', views.LiveLogsPartialView.as_view(), name='nginxmon-live-logs'),
path('api/alerts/', views.AlertsPartialView.as_view(), name='nginxmon-alerts-partial'),
diff --git a/nginxmon/views.py b/nginxmon/views.py
index ef3e5f3..2283cb9 100644
--- a/nginxmon/views.py
+++ b/nginxmon/views.py
@@ -327,6 +327,47 @@ class UnbanIPView(View):
return redirect('nginxmon-dashboard')
+class IPBanView(View):
+ """Manual IP ban form — adds IPs/CIDRs to the k8s ConfigMap and creates BannedIP records."""
+
+ def post(self, request):
+ raw = request.POST.get('ips', '').strip()
+ if not raw:
+ messages.error(request, 'No IPs provided.')
+ return redirect('nginxmon-dashboard')
+
+ new_ips = [
+ ip.strip()
+ for part in raw.replace('\n', ',').split(',')
+ for ip in [part.strip()]
+ if ip
+ ]
+
+ try:
+ from links.mini_apps_views import _read_blocked_ips, _write_blocked_ips
+ blocked = _read_blocked_ips()
+ added = 0
+ for ip in new_ips:
+ if ip not in blocked:
+ blocked.append(ip)
+ added += 1
+ BannedIP.objects.get_or_create(
+ ip=ip,
+ defaults=dict(
+ ban_source='manual',
+ reason='Manually banned from dashboard.',
+ request_count=NginxAccessLog.objects.filter(remote_addr=ip).count(),
+ ),
+ )
+ _write_blocked_ips(blocked)
+ messages.success(request, f'Banned {added} new IP(s). Total blocked: {len(blocked)}.')
+ except Exception as exc:
+ logger.error('nginxmon IPBanView: %s', exc)
+ messages.error(request, f'Failed to ban IPs: {exc}')
+
+ return redirect('nginxmon-dashboard')
+
+
# ── Chart & ingest API ────────────────────────────────────────────────────────
class ChartDataView(View):