Add IP autoban system: banned_ips table, global ipGuard middleware, 24h bans on honeypot fill / 10 failed logins / 10 wrong share passwords; trust proxy for real client IPs behind funnel; admin API and settings UI to manage active bans

This commit is contained in:
dev
2026-09-12 00:35:51 +03:00
parent 2bebfc071c
commit 2c7ec17c8b
5 changed files with 145 additions and 1 deletions
+23
View File
@@ -238,6 +238,28 @@ document.getElementById('restoreFile').addEventListener('change', async (e) => {
e.target.value = '';
});
async function loadBans() {
const el = document.getElementById('bansList');
const res = await fetch(`${API}/api/bans`, { headers: hdr() });
if (res.status === 403) { document.getElementById('bansCard').style.display = 'none'; return; }
if (!res.ok) { el.innerHTML = '<span class="chip">Не удалось загрузить</span>'; return; }
const bans = await res.json();
if (!bans.length) { el.innerHTML = '<span class="chip">Активных блокировок нет</span>'; return; }
el.innerHTML = bans.map(b => `
<span class="chip">
<b>${esc(b.ip)}</b> — ${esc(b.reason)} до ${new Date(b.banned_until).toLocaleString('ru')}
<button type="button" data-unban="${esc(b.ip)}" title="Разблокировать" style="background:none;border:none;color:var(--primary);cursor:pointer;font-weight:700">✕</button>
</span>`).join('');
}
document.getElementById('bansList').addEventListener('click', async e => {
const ip = e.target.closest('[data-unban]')?.dataset.unban;
if (!ip) return;
const res = await fetch(`${API}/api/bans/${encodeURIComponent(ip)}`, { method: 'DELETE', headers: hdr() });
if (res.ok) { showToast('IP разблокирован'); loadBans(); }
else showToast('Ошибка разблокировки');
});
document.getElementById('backupBtn').addEventListener('click', downloadBackup);
document.getElementById('restoreBtn').addEventListener('click', () => document.getElementById('restoreFile').click());
document.getElementById('saveBtn').addEventListener('click', saveSettings);
@@ -247,5 +269,6 @@ document.getElementById('saveBtn').addEventListener('click', saveSettings);
buildSidebar(document.body.dataset.page);
bindDirty();
loadSettings();
loadBans();
}
})();
+9
View File
@@ -138,6 +138,15 @@
</div>
<div class="restore-status" id="restoreStatus"></div>
</div>
<div class="settings-card" id="bansCard">
<div class="card-head">
<div class="ic"><i data-lucide="shield-off"></i></div>
<h3>Блокировки IP</h3>
</div>
<p class="hint">IP автоматически блокируется на 24 часа: за заполнение скрытого антиспам-поля, за 10 неудачных попыток входа или 10 неверных паролей на защищённой ссылке.</p>
<div class="data-stats" id="bansList"><span class="chip">Загрузка…</span></div>
</div>
</div>
</div>
</div>