Update ban ip form

This commit is contained in:
2026-05-11 22:57:25 +10:00
parent 81c0e807b9
commit 839aadcf21
5 changed files with 91 additions and 9 deletions
-8
View File
@@ -40,14 +40,6 @@ class MiniAppsListView(TemplateView):
'icon': 'fas fa-chart-line',
'color': '#27ae60'
},
{
'name': 'Nginx IP Ban',
'description': 'Manage the nginx ingress block list — view, add, and remove banned IPs and CIDR ranges directly from the Kubernetes ConfigMap.',
'url': 'mini-apps-ip-ban',
'thumbnail': 'https://images.unsplash.com/photo-1614064641938-3bbee52942c7?w=400&h=300&fit=crop',
'icon': 'fas fa-ban',
'color': '#e74c3c'
},
{
'name': 'Weather Dashboard',
'description': 'Real-time weather information with beautiful visualizations and forecasts.',
-1
View File
@@ -90,7 +90,6 @@ class LinkListView(ListView):
mini_apps = [
{'name': 'Image Gallery', 'description': 'Browse Images', 'url': 'mini-apps-image-gallery'},
{'name': 'TTS', 'description': 'Convert text to speech', 'url': 'mini-apps-tts'},
{'name': 'IP Ban', 'description': 'Block IP addresses', 'url': 'mini-apps-ip-ban'},
]
context['mini_apps'] = [
{**app, 'color': random.choice(colors)}
@@ -57,6 +57,10 @@
<i class="fas fa-search mr-1.5"></i> Detect Threats
</button>
</form>
<button onclick="document.getElementById('ip-ban-modal').classList.remove('hidden')"
class="inline-flex items-center px-3 py-1.5 bg-red-600 text-white text-xs rounded-md hover:bg-red-700 font-medium">
<i class="fas fa-ban mr-1.5"></i> Ban IP
</button>
<a href="{% url 'nginxmon-profile-create' %}"
class="inline-flex items-center px-3 py-1.5 bg-indigo-600 text-white text-xs rounded-md hover:bg-indigo-700 font-medium">
<i class="fas fa-plus mr-1.5"></i> New Alert Profile
@@ -261,6 +265,49 @@
</div>
</div>
<!-- ── IP Ban Modal ──────────────────────────────────────────────────────── -->
<div id="ip-ban-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center p-4"
onclick="if(event.target===this)this.classList.add('hidden')">
<div class="absolute inset-0 bg-black/40"></div>
<div class="relative bg-white rounded-xl shadow-2xl w-full max-w-lg">
<div class="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
<div>
<h2 class="text-sm font-semibold text-gray-900 flex items-center gap-2">
<i class="fas fa-ban text-red-500"></i> Ban IP / CIDR
</h2>
<p class="text-xs text-gray-400 mt-0.5">
Adds to <code class="bg-gray-100 px-1 rounded text-xs">block-cidrs-manual</code> ConfigMap.
Separate multiple entries with commas or newlines.
</p>
</div>
<button onclick="document.getElementById('ip-ban-modal').classList.add('hidden')"
class="text-gray-400 hover:text-gray-600 text-lg leading-none ml-4">&times;</button>
</div>
<form method="post" action="{% url 'nginxmon-ip-ban' %}" class="p-6 space-y-4">
{% csrf_token %}
<textarea name="ips" rows="4" autofocus
placeholder="1.2.3.4&#10;5.6.7.0/24&#10;8.8.8.8, 9.9.9.9"
class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm font-mono focus:outline-none focus:ring-2 focus:ring-red-400 focus:border-transparent resize-none"></textarea>
<div class="flex items-center justify-between">
<p class="text-xs text-gray-400">
<i class="fas fa-info-circle mr-1"></i>Duplicates are skipped automatically.
</p>
<div class="flex gap-2">
<button type="button"
onclick="document.getElementById('ip-ban-modal').classList.add('hidden')"
class="px-4 py-2 text-xs rounded-md bg-gray-100 text-gray-600 hover:bg-gray-200 font-medium">
Cancel
</button>
<button type="submit"
class="inline-flex items-center px-5 py-2 bg-red-600 text-white text-xs font-medium rounded-md hover:bg-red-700 transition-colors">
<i class="fas fa-ban mr-1.5"></i> Ban IPs
</button>
</div>
</div>
</form>
</div>
</div>
<!-- ── Banned IPs ─────────────────────────────────────────────────────── -->
<div class="bg-white shadow-sm rounded-lg border border-gray-200 overflow-hidden">
<div class="px-6 py-4 bg-red-50 border-b border-red-200 flex items-center justify-between">
+3
View File
@@ -20,6 +20,9 @@ urlpatterns = [
path('alert/<int:pk>/dismiss/', views.DismissAlertView.as_view(), name='nginxmon-dismiss-alert'),
path('ban/<int:pk>/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'),
+41
View File
@@ -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):