2

Add review filter and promote decisions panel

This commit is contained in:
2026-03-12 12:01:09 +01:00
parent 8b744d31f3
commit 34d6d3ddcb
2 changed files with 58 additions and 11 deletions

View File

@@ -399,6 +399,7 @@ const overviewHTML = `<!doctype html>
<div class="toolbar-right">
<label class="toggle"><input id="show-bots-toggle" type="checkbox" checked onchange="toggleKnownBots()">Show known bots</label>
<label class="toggle"><input id="show-allowed-toggle" type="checkbox" checked onchange="toggleAllowed()">Show allowed</label>
<label class="toggle"><input id="show-review-toggle" type="checkbox" onchange="toggleReviewOnly()">Review only</label>
<div class="meta">Last 24 hours · click a column to sort</div>
</div>
</div>
@@ -423,6 +424,7 @@ const overviewHTML = `<!doctype html>
const storageKeys = {
showKnownBots: 'caddy-opnsense-blocker.overview.showKnownBots',
showAllowed: 'caddy-opnsense-blocker.overview.showAllowed',
showReviewOnly: 'caddy-opnsense-blocker.overview.showReviewOnly',
sortKey: 'caddy-opnsense-blocker.overview.sortKey',
sortDirection: 'caddy-opnsense-blocker.overview.sortDirection',
};
@@ -439,6 +441,7 @@ const overviewHTML = `<!doctype html>
let currentSort = loadSortPreference();
let showKnownBots = loadShowKnownBotsPreference();
let showAllowed = loadShowAllowedPreference();
let showReviewOnly = loadShowReviewOnlyPreference();
function renderStats(data) {
const stats = [
@@ -499,6 +502,25 @@ const overviewHTML = `<!doctype html>
}
}
function loadShowReviewOnlyPreference() {
try {
const rawValue = window.localStorage.getItem(storageKeys.showReviewOnly);
if (rawValue === null) {
return false;
}
return rawValue === 'true';
} catch (_) {
return false;
}
}
function saveShowReviewOnlyPreference(value) {
try {
window.localStorage.setItem(storageKeys.showReviewOnly, value ? 'true' : 'false');
} catch (_) {
}
}
function loadSortPreference() {
const fallback = { key: 'events', direction: 'desc' };
try {
@@ -593,6 +615,10 @@ const overviewHTML = `<!doctype html>
if (allowedToggle) {
allowedToggle.checked = showAllowed;
}
const reviewToggle = document.getElementById('show-review-toggle');
if (reviewToggle) {
reviewToggle.checked = showReviewOnly;
}
document.querySelectorAll('button[data-sort]').forEach(button => {
const key = button.dataset.sort;
const active = key === currentSort.key;
@@ -645,7 +671,7 @@ const overviewHTML = `<!doctype html>
}
function renderIPs(items) {
const filteredItems = items.filter(item => (showKnownBots || !item.bot) && (showAllowed || item.state !== 'allowed'));
const filteredItems = items.filter(item => (showKnownBots || !item.bot) && (showAllowed || item.state !== 'allowed') && (!showReviewOnly || item.state === 'review'));
const rows = sortItems(filteredItems).map(item => [
'<tr>',
' <td class="mono"><div class="ip-cell">' + renderBotChip(item.bot) + '<a href="/ips/' + encodeURIComponent(item.ip) + '">' + escapeHtml(item.ip) + '</a></div></td>',
@@ -658,7 +684,9 @@ const overviewHTML = `<!doctype html>
'</tr>'
].join(''));
let emptyMessage = 'No IPs seen in the last 24 hours.';
if (!showKnownBots && !showAllowed) {
if (showReviewOnly) {
emptyMessage = 'No review IPs match the current filters in the last 24 hours.';
} else if (!showKnownBots && !showAllowed) {
emptyMessage = 'No non-bot, non-allowed IPs seen in the last 24 hours.';
} else if (!showKnownBots) {
emptyMessage = 'No non-bot IPs seen in the last 24 hours.';
@@ -698,6 +726,13 @@ const overviewHTML = `<!doctype html>
render();
}
function toggleReviewOnly() {
const toggle = document.getElementById('show-review-toggle');
showReviewOnly = !!toggle && toggle.checked;
saveShowReviewOnlyPreference(showReviewOnly);
render();
}
async function sendAction(ip, action, promptLabel) {
const reason = window.prompt(promptLabel, '');
if (reason === null) {
@@ -807,15 +842,6 @@ const ipDetailsHTML = `<!doctype html>
<h2>Investigation</h2>
<div id="investigation" class="kv"></div>
</section>
<section class="panel">
<h2>Requests from this IP</h2>
<table>
<thead>
<tr><th>Time</th><th>Source</th><th>Host</th><th>Method</th><th>URI</th><th>Status</th><th>Decision</th><th>User agent</th></tr>
</thead>
<tbody id="events-body"></tbody>
</table>
</section>
<section class="panel">
<h2>Decisions</h2>
<table>
@@ -825,6 +851,15 @@ const ipDetailsHTML = `<!doctype html>
<tbody id="decisions-body"></tbody>
</table>
</section>
<section class="panel">
<h2>Requests from this IP</h2>
<table>
<thead>
<tr><th>Time</th><th>Source</th><th>Host</th><th>Method</th><th>URI</th><th>Status</th><th>Decision</th><th>User agent</th></tr>
</thead>
<tbody id="events-body"></tbody>
</table>
</section>
<section class="panel">
<h2>Backend actions</h2>
<table>