You've already forked caddy-opnsense-blocker
Polish the requests log table layout
This commit is contained in:
@@ -930,15 +930,17 @@ const queryLogHTML = `<!doctype html>
|
|||||||
.status.review { background: #78350f; }
|
.status.review { background: #78350f; }
|
||||||
.status.allowed { background: #14532d; }
|
.status.allowed { background: #14532d; }
|
||||||
.status.observed { background: #1e293b; }
|
.status.observed { background: #1e293b; }
|
||||||
|
.status-code { display: inline-flex; align-items: center; justify-content: center; min-width: 3.5rem; padding: .15rem .45rem; border-radius: 999px; font-size: .8rem; font-weight: 700; background: #1e293b; color: #e2e8f0; }
|
||||||
|
.status-code.client-error { background: #713f12; color: #fde68a; }
|
||||||
|
.status-code.server-error { background: #9a3412; color: #fdba74; }
|
||||||
.method { display: inline-block; padding: .2rem .45rem; border-radius: 999px; font-size: .78rem; font-weight: 700; }
|
.method { display: inline-block; padding: .2rem .45rem; border-radius: 999px; font-size: .78rem; font-weight: 700; }
|
||||||
.method.get { background: #14532d; color: #dcfce7; }
|
.method.get { background: #14532d; color: #dcfce7; }
|
||||||
.method.post { background: #78350f; color: #fef3c7; }
|
.method.post { background: #78350f; color: #fef3c7; }
|
||||||
.method.head { background: #0c4a6e; color: #e0f2fe; }
|
.method.head { background: #0c4a6e; color: #e0f2fe; }
|
||||||
.method.other { background: #334155; color: #e2e8f0; }
|
.method.other { background: #334155; color: #e2e8f0; }
|
||||||
.actions { display: flex; gap: .35rem; flex-wrap: nowrap; white-space: nowrap; }
|
.actions { display: block; }
|
||||||
.action-link, button { display: inline-flex; align-items: center; justify-content: center; gap: .35rem; border-radius: .45rem; padding: .3rem .6rem; font-size: .9rem; }
|
.actions .muted { display: block; text-align: center; }
|
||||||
.action-link, button { white-space: nowrap; }
|
button { display: block; width: 100%; min-width: 7rem; align-items: center; justify-content: center; gap: .35rem; border-radius: .45rem; padding: .3rem .6rem; font-size: .9rem; white-space: nowrap; }
|
||||||
.action-link { background: #1e293b; color: #e2e8f0; text-decoration: none; }
|
|
||||||
button { background: #2563eb; color: white; border: 0; cursor: pointer; }
|
button { background: #2563eb; color: white; border: 0; cursor: pointer; }
|
||||||
button.secondary { background: #475569; }
|
button.secondary { background: #475569; }
|
||||||
button.danger { background: #dc2626; }
|
button.danger { background: #dc2626; }
|
||||||
@@ -1014,9 +1016,9 @@ const queryLogHTML = `<!doctype html>
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-time">Time</th>
|
<th class="col-time">Time</th>
|
||||||
<th class="col-source">Source</th>
|
|
||||||
<th class="col-ip">IP</th>
|
<th class="col-ip">IP</th>
|
||||||
<th class="col-method">Method</th>
|
<th class="col-method">Method</th>
|
||||||
|
<th class="col-source">Source</th>
|
||||||
<th class="col-request">Request</th>
|
<th class="col-request">Request</th>
|
||||||
<th class="col-status">Status</th>
|
<th class="col-status">Status</th>
|
||||||
<th class="col-state">State</th>
|
<th class="col-state">State</th>
|
||||||
@@ -1119,13 +1121,24 @@ const queryLogHTML = `<!doctype html>
|
|||||||
|
|
||||||
function renderActions(item) {
|
function renderActions(item) {
|
||||||
const actions = item.actions || {};
|
const actions = item.actions || {};
|
||||||
const buttons = ['<a class="action-link" href="/ips/' + encodeURIComponent(item.client_ip) + '">Open</a>'];
|
|
||||||
if (actions.can_unblock) {
|
if (actions.can_unblock) {
|
||||||
buttons.push('<button class="secondary" data-ip="' + escapeHtml(item.client_ip) + '" onclick="sendAction(this.dataset.ip, \'unblock\', \'Reason for manual unblock\')">Unblock</button>');
|
return '<div class="actions"><button class="secondary" data-ip="' + escapeHtml(item.client_ip) + '" onclick="sendAction(this.dataset.ip, \'unblock\', \'Reason for manual unblock\')">Unblock</button></div>';
|
||||||
} else if (actions.can_block) {
|
|
||||||
buttons.push('<button class="danger" data-ip="' + escapeHtml(item.client_ip) + '" onclick="sendAction(this.dataset.ip, \'block\', \'Reason for manual block\')">Block</button>');
|
|
||||||
}
|
}
|
||||||
return '<div class="actions">' + buttons.join('') + '</div>';
|
if (actions.can_block) {
|
||||||
|
return '<div class="actions"><button class="danger" data-ip="' + escapeHtml(item.client_ip) + '" onclick="sendAction(this.dataset.ip, \'block\', \'Reason for manual block\')">Block</button></div>';
|
||||||
|
}
|
||||||
|
return '<div class="actions"><span class="muted">—</span></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusCodeClass(status) {
|
||||||
|
const code = Number(status || 0);
|
||||||
|
if (code >= 500) {
|
||||||
|
return 'server-error';
|
||||||
|
}
|
||||||
|
if (code >= 400) {
|
||||||
|
return 'client-error';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyToggles() {
|
function applyToggles() {
|
||||||
@@ -1217,15 +1230,15 @@ const queryLogHTML = `<!doctype html>
|
|||||||
function renderEvents(payload) {
|
function renderEvents(payload) {
|
||||||
const items = Array.isArray(payload.items) ? payload.items : [];
|
const items = Array.isArray(payload.items) ? payload.items : [];
|
||||||
const rows = items.map(item => {
|
const rows = items.map(item => {
|
||||||
const requestLabel = ((item.host || '') ? (item.host + item.uri) : (item.uri || '—'));
|
const requestLabel = item.uri || '—';
|
||||||
return [
|
return [
|
||||||
'<tr>',
|
'<tr>',
|
||||||
' <td class="col-time">' + escapeHtml(formatDate(item.occurred_at)) + '</td>',
|
' <td class="col-time">' + escapeHtml(formatDate(item.occurred_at)) + '</td>',
|
||||||
' <td class="col-source">' + escapeHtml(item.source_name || '—') + '</td>',
|
|
||||||
' <td class="col-ip mono"><div class="ip-cell">' + renderBotChip(item.bot) + '<a href="/ips/' + encodeURIComponent(item.client_ip) + '">' + escapeHtml(item.client_ip || '—') + '</a></div></td>',
|
' <td class="col-ip mono"><div class="ip-cell">' + renderBotChip(item.bot) + '<a href="/ips/' + encodeURIComponent(item.client_ip) + '">' + escapeHtml(item.client_ip || '—') + '</a></div></td>',
|
||||||
' <td class="col-method"><span class="method ' + escapeHtml(methodClass(item.method)) + '">' + escapeHtml(item.method || 'OTHER') + '</span></td>',
|
' <td class="col-method"><span class="method ' + escapeHtml(methodClass(item.method)) + '">' + escapeHtml(item.method || 'OTHER') + '</span></td>',
|
||||||
|
' <td class="col-source">' + escapeHtml(item.source_name || '—') + '</td>',
|
||||||
' <td class="col-request mono"><span class="request-text" title="' + escapeHtml(requestLabel) + '">' + escapeHtml(requestLabel) + '</span></td>',
|
' <td class="col-request mono"><span class="request-text" title="' + escapeHtml(requestLabel) + '">' + escapeHtml(requestLabel) + '</span></td>',
|
||||||
' <td class="col-status">' + escapeHtml(String(item.status || 0)) + '</td>',
|
' <td class="col-status"><span class="status-code ' + escapeHtml(statusCodeClass(item.status)) + '">' + escapeHtml(String(item.status || 0)) + '</span></td>',
|
||||||
' <td class="col-state"><span class="status ' + escapeHtml(item.current_state || 'observed') + '">' + escapeHtml(item.current_state || 'observed') + '</span></td>',
|
' <td class="col-state"><span class="status ' + escapeHtml(item.current_state || 'observed') + '">' + escapeHtml(item.current_state || 'observed') + '</span></td>',
|
||||||
' <td class="col-reason"><span class="reason-text" title="' + escapeHtml(item.decision_reason || '—') + '">' + escapeHtml(item.decision_reason || '—') + '</span></td>',
|
' <td class="col-reason"><span class="reason-text" title="' + escapeHtml(item.decision_reason || '—') + '">' + escapeHtml(item.decision_reason || '—') + '</span></td>',
|
||||||
' <td class="col-actions">' + renderActions(item) + '</td>',
|
' <td class="col-actions">' + renderActions(item) + '</td>',
|
||||||
|
|||||||
Reference in New Issue
Block a user