Files
crowdsec-dashy/web/templates/pages/alerts.html
T

160 lines
6.4 KiB
HTML
Raw Normal View History

2026-05-17 08:28:16 +00:00
{{template "base" .}}
{{define "content"}}
<div style="max-width:1400px">
<div style="margin-bottom:16px">
<div class="page-title">Alerts</div>
<div class="page-sub">Security events detected by CrowdSec agents</div>
</div>
<div class="panel" style="margin-bottom:16px">
<div class="panel-body" style="padding:12px 18px">
2026-05-17 14:12:06 +00:00
<form method="GET" action="/alerts" class="filter-bar" id="filter-form">
2026-05-17 08:28:16 +00:00
<input class="filter-input" type="text" name="scenario" placeholder="Scenario filter..." value="{{.Filter.Scenario}}">
<input class="filter-input" type="text" name="ip" placeholder="Source IP..." value="{{.Filter.IP}}">
<select class="filter-select" name="since">
<option value="">All time</option>
<option value="1h" {{if eq .Filter.Since "1h"}}selected{{end}}>Last 1h</option>
<option value="24h" {{if eq .Filter.Since "24h"}}selected{{end}}>Last 24h</option>
<option value="7d" {{if eq .Filter.Since "7d"}}selected{{end}}>Last 7d</option>
</select>
2026-05-17 14:12:06 +00:00
{{if .ShowUpdates}}<input type="hidden" name="show_updates" value="1">{{end}}
2026-05-17 08:28:16 +00:00
<button type="submit" class="btn-secondary">Filter</button>
2026-05-17 14:12:06 +00:00
{{if or .Filter.Scenario .Filter.IP .Filter.Since}}<a href="/alerts{{if .ShowUpdates}}?show_updates=1{{end}}" class="btn-ghost">Clear</a>{{end}}
2026-05-17 08:28:16 +00:00
</form>
</div>
</div>
<div class="panel">
<form method="POST" action="/alerts/delete" id="bulk-form">
<input type="hidden" name="_csrf" value="{{.CSRFToken}}">
<div class="panel-header">
2026-05-17 14:12:06 +00:00
<span class="panel-title">Alerts <span id="visible-count"></span></span>
2026-05-17 08:28:16 +00:00
<div style="display:flex;gap:8px;align-items:center">
2026-05-17 14:12:06 +00:00
<button type="button" class="btn-ghost-sm" id="toggle-updates" onclick="toggleUpdates()">{{if .ShowUpdates}}Hide updates{{else}}Show updates{{end}}</button>
2026-05-17 08:28:16 +00:00
<button type="button" class="btn-ghost-sm" onclick="toggleAll()">Select all</button>
<button type="submit" class="btn-danger-sm" onclick="return confirmBulkDelete()">Delete selected</button>
</div>
</div>
{{if .Alerts}}
<div style="overflow-x:auto">
<table class="data-table">
<thead>
<tr>
<th style="width:36px"><input type="checkbox" id="chk-all" onchange="selectAll(this)"></th>
<th>ID</th>
<th>Scenario</th>
<th>Source</th>
<th>Country</th>
<th>Events</th>
<th>Decisions</th>
<th>Date</th>
<th>Action</th>
</tr>
</thead>
2026-05-17 14:12:06 +00:00
<tbody id="alerts-tbody">
2026-05-17 08:28:16 +00:00
{{range .Alerts}}
2026-05-17 14:12:06 +00:00
<tr data-scenario="{{.Scenario}}">
2026-05-17 08:28:16 +00:00
<td><input type="checkbox" name="id" value="{{.ID}}" class="row-chk"></td>
<td style="font-family:'JetBrains Mono',monospace;font-size:12px">{{.ID}}</td>
2026-05-17 14:12:06 +00:00
<td style="font-size:12px" title="{{.Scenario}}">{{truncate .Scenario 40}}</td>
2026-05-17 08:28:16 +00:00
<td style="font-family:'JetBrains Mono',monospace;font-size:12px">{{.Source.Value}}</td>
<td style="font-size:12px;color:var(--muted)">{{.Source.CN}}</td>
<td style="font-family:'JetBrains Mono',monospace">{{.EventsCount}}</td>
<td style="font-family:'JetBrains Mono',monospace">{{len .Decisions}}</td>
<td style="font-size:12px;color:var(--muted)">{{truncate .StartAt 16}}</td>
<td>
<form method="POST" action="/alerts/delete" style="display:inline">
<input type="hidden" name="_csrf" value="{{$.CSRFToken}}">
<input type="hidden" name="id" value="{{.ID}}">
<button type="submit" class="btn-danger-sm" onclick="return confirm('Delete alert #{{.ID}}?')">Delete</button>
</form>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
2026-05-17 14:12:06 +00:00
<div style="display:flex;align-items:center;justify-content:space-between;padding:12px 18px;border-top:1px solid var(--border)">
<div style="font-size:12px;color:var(--muted)">Page {{.Page}} &middot; {{len .Alerts}} per page</div>
<div style="display:flex;gap:8px">
{{if gt .Page 1}}
<a href="{{alertsPageURL .Filter .Page false .ShowUpdates}}" class="btn-ghost-sm">Previous</a>
{{end}}
{{if .HasNext}}
<a href="{{alertsPageURL .Filter .Page true .ShowUpdates}}" class="btn-ghost-sm">Next</a>
{{end}}
</div>
</div>
2026-05-17 08:28:16 +00:00
{{else}}
<div class="empty-state">
<div class="empty-text">No alerts found</div>
<div class="empty-sub">No security events match the current filters</div>
</div>
{{end}}
</form>
</div>
</div>
{{end}}
{{define "scripts"}}
<script>
2026-05-17 14:12:06 +00:00
var showUpdates = {{if .ShowUpdates}}true{{else}}false{{end}};
function applyFilter() {
var rows = document.querySelectorAll('#alerts-tbody tr');
var visible = 0;
rows.forEach(function(row) {
var scenario = row.getAttribute('data-scenario') || '';
var isUpdate = scenario.startsWith('update :') || scenario.startsWith('update:');
if (!showUpdates && isUpdate) {
row.style.display = 'none';
} else {
row.style.display = '';
visible++;
}
});
var el = document.getElementById('visible-count');
if (el) el.textContent = '(' + visible + ' shown)';
}
function toggleUpdates() {
showUpdates = !showUpdates;
var btn = document.getElementById('toggle-updates');
if (btn) btn.textContent = showUpdates ? 'Hide updates' : 'Show updates';
applyFilter();
// persist across pages: update filter form hidden input
var form = document.getElementById('filter-form');
var existing = form.querySelector('input[name="show_updates"]');
if (showUpdates) {
if (!existing) {
var inp = document.createElement('input');
inp.type = 'hidden'; inp.name = 'show_updates'; inp.value = '1';
form.appendChild(inp);
}
} else {
if (existing) existing.remove();
}
}
2026-05-17 08:28:16 +00:00
function selectAll(cb) {
document.querySelectorAll('.row-chk').forEach(function(c) { c.checked = cb.checked; });
}
function toggleAll() {
var cb = document.getElementById('chk-all');
cb.checked = !cb.checked;
selectAll(cb);
}
function confirmBulkDelete() {
var n = document.querySelectorAll('.row-chk:checked').length;
if (n === 0) { alert('Select at least one alert.'); return false; }
return confirm('Delete ' + n + ' alert(s)?');
}
2026-05-17 14:12:06 +00:00
applyFilter();
2026-05-17 08:28:16 +00:00
</script>
{{end}}