Files
GuacPanel/webgui/templates/fail2ban.html
T
njomac 50c125e0db Initial commit — GuacPanel v1.0
All-in-one installer za Apache Guacamole s Nginx reverse proxy,
fail2ban, SSL (Let's Encrypt / Self-signed), GeoIP blokiranjem
i Flask web GUI za upravljanje.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 11:21:44 +02:00

125 lines
4.0 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends 'base.html' %}
{% block title %}fail2ban Guacamole Manager{% endblock %}
{% block content %}
<h4 class="mb-4"><i class="bi bi-shield-x me-2"></i>fail2ban</h4>
<div class="row g-4">
<!-- Zabanjeni IP-ovi -->
<div class="col-md-8">
{% for jail, ips in banned.items() %}
<div class="card mb-3">
<div class="card-header d-flex justify-content-between">
<span class="fw-semibold">{{ jail }}</span>
<span class="badge {{ 'bg-danger' if ips else 'bg-secondary' }}">
{{ ips|length }} zabanjeno
</span>
</div>
<div class="card-body p-0">
{% if ips %}
<table class="table table-sm mb-0" style="border-color:#30363d">
<thead class="table-dark">
<tr>
<th>IP adresa</th>
<th class="text-end">Akcija</th>
</tr>
</thead>
<tbody>
{% for ip in ips %}
<tr>
<td class="font-monospace">{{ ip }}</td>
<td class="text-end">
<button class="btn btn-outline-success btn-sm"
onclick="unban('{{ ip }}', '{{ jail }}')">
<i class="bi bi-check-circle"></i> Odbani
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted small p-3 mb-0">Nema zabanjenih IP-ova</p>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<!-- Akcije i postavke -->
<div class="col-md-4">
<!-- Ručni ban -->
<div class="card mb-3">
<div class="card-header fw-semibold">
<i class="bi bi-shield-plus me-1"></i> Ručni ban
</div>
<div class="card-body">
<div class="mb-2">
<input id="ban-ip" type="text" class="form-control form-control-sm"
placeholder="192.168.1.100">
</div>
<select id="ban-jail" class="form-select form-select-sm mb-2">
{% for j in banned.keys() %}
<option>{{ j }}</option>
{% endfor %}
</select>
<button class="btn btn-danger btn-sm w-100" onclick="banIP()">
<i class="bi bi-ban"></i> Baniraj IP
</button>
</div>
</div>
<!-- Postavke -->
<div class="card">
<div class="card-header fw-semibold">
<i class="bi bi-sliders me-1"></i> Postavke
</div>
<div class="card-body">
<div class="mb-2">
<label class="form-label small text-muted">Max pokušaja (maxretry)</label>
<input id="maxretry" type="number" class="form-control form-control-sm"
value="{{ cfg.get('maxretry', 5) }}" min="1" max="20">
</div>
<div class="mb-3">
<label class="form-label small text-muted">Trajanje bana (sekunde)</label>
<input id="bantime" type="number" class="form-control form-control-sm"
value="{{ cfg.get('bantime', 3600) }}" min="60">
<small class="text-muted">3600=1h · 86400=1dan · 604800=1tjedan</small>
</div>
<button class="btn btn-primary btn-sm w-100" onclick="saveSettings()">
<i class="bi bi-floppy"></i> Spremi
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
async function unban(ip, jail) {
const r = await api('/api/fail2ban/unban', {ip, jail});
showToast(r.msg, r.ok);
if (r.ok) setTimeout(() => location.reload(), 1000);
}
async function banIP() {
const ip = document.getElementById('ban-ip').value.trim();
const jail = document.getElementById('ban-jail').value;
if (!ip) return showToast('Unesite IP adresu', false);
const r = await api('/api/fail2ban/ban', {ip, jail});
showToast(r.msg, r.ok);
if (r.ok) setTimeout(() => location.reload(), 1000);
}
async function saveSettings() {
const r = await api('/api/fail2ban/settings', {
maxretry: document.getElementById('maxretry').value,
bantime: document.getElementById('bantime').value
});
showToast(r.msg, r.ok);
}
</script>
{% endblock %}