mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-19 06:53:14 +02:00
Improve ban events search search through all db-entries and also implement pagination to load more events
This commit is contained in:
@@ -68,6 +68,7 @@
|
||||
"logs.overview.per_server_empty": "Noch keine Serverdaten verfügbar.",
|
||||
"logs.overview.recent_filtered_empty": "Keine gespeicherten Ereignisse passen zu den Filtern.",
|
||||
"logs.overview.recent_count_label": "Angezeigte Ereignisse",
|
||||
"logs.overview.load_more": "Mehr laden",
|
||||
"logs.overview.country_unknown": "Unbekannt",
|
||||
"logs.overview.last_seen": "Zuletzt gesehen",
|
||||
"logs.table.server": "Server",
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"logs.overview.per_server_empty": "No keni Serverdate verfügbar.",
|
||||
"logs.overview.recent_filtered_empty": "Kei Ereigniss erfülle d Filter.",
|
||||
"logs.overview.recent_count_label": "Aazeigti Ereigniss",
|
||||
"logs.overview.load_more": "Mehr lade",
|
||||
"logs.overview.country_unknown": "Unbekannt",
|
||||
"logs.overview.last_seen": "Zletscht gseh",
|
||||
"logs.table.server": "Server",
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"logs.overview.per_server_empty": "No per-server data available yet.",
|
||||
"logs.overview.recent_filtered_empty": "No stored events match the current filters.",
|
||||
"logs.overview.recent_count_label": "Events shown",
|
||||
"logs.overview.load_more": "Load more",
|
||||
"logs.overview.country_unknown": "Unknown",
|
||||
"logs.overview.last_seen": "Last seen",
|
||||
"logs.table.server": "Server",
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"logs.overview.per_server_empty": "Aún no hay datos por servidor.",
|
||||
"logs.overview.recent_filtered_empty": "No hay eventos que coincidan con los filtros.",
|
||||
"logs.overview.recent_count_label": "Eventos mostrados",
|
||||
"logs.overview.load_more": "Cargar más",
|
||||
"logs.overview.country_unknown": "Desconocido",
|
||||
"logs.overview.last_seen": "Última vez",
|
||||
"logs.table.server": "Servidor",
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"logs.overview.per_server_empty": "Aucune donnée par serveur pour le moment.",
|
||||
"logs.overview.recent_filtered_empty": "Aucun événement ne correspond aux filtres.",
|
||||
"logs.overview.recent_count_label": "Événements affichés",
|
||||
"logs.overview.load_more": "Charger plus",
|
||||
"logs.overview.country_unknown": "Inconnu",
|
||||
"logs.overview.last_seen": "Dernière apparition",
|
||||
"logs.table.server": "Serveur",
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"logs.overview.per_server_empty": "Ancora nessun dato per server.",
|
||||
"logs.overview.recent_filtered_empty": "Nessun evento corrisponde ai filtri.",
|
||||
"logs.overview.recent_count_label": "Eventi mostrati",
|
||||
"logs.overview.load_more": "Carica altri",
|
||||
"logs.overview.country_unknown": "Sconosciuto",
|
||||
"logs.overview.last_seen": "Ultima visualizzazione",
|
||||
"logs.table.server": "Server",
|
||||
|
||||
@@ -603,6 +603,139 @@ WHERE 1=1`
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
const (
|
||||
// MaxBanEventsLimit is the maximum number of events per API request (pagination page size).
|
||||
MaxBanEventsLimit = 50
|
||||
// MaxBanEventsOffset is the maximum offset (total events loaded in UI capped for browser stability).
|
||||
MaxBanEventsOffset = 1000
|
||||
)
|
||||
|
||||
// ListBanEventsFiltered returns ban events with optional search and country filter, ordered by occurred_at DESC.
|
||||
// search is applied as LIKE %search% on ip, jail, server_name, hostname, country.
|
||||
// limit is capped at MaxBanEventsLimit; offset is capped at MaxBanEventsOffset.
|
||||
func ListBanEventsFiltered(ctx context.Context, serverID string, limit, offset int, since time.Time, search, country string) ([]BanEventRecord, error) {
|
||||
if db == nil {
|
||||
return nil, errors.New("storage not initialised")
|
||||
}
|
||||
if limit <= 0 || limit > MaxBanEventsLimit {
|
||||
limit = MaxBanEventsLimit
|
||||
}
|
||||
if offset < 0 || offset > MaxBanEventsOffset {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
baseQuery := `
|
||||
SELECT id, server_id, server_name, jail, ip, country, hostname, failures, whois, logs, event_type, occurred_at, created_at
|
||||
FROM ban_events
|
||||
WHERE 1=1`
|
||||
args := []any{}
|
||||
|
||||
if serverID != "" {
|
||||
baseQuery += " AND server_id = ?"
|
||||
args = append(args, serverID)
|
||||
}
|
||||
if !since.IsZero() {
|
||||
baseQuery += " AND occurred_at >= ?"
|
||||
args = append(args, since.UTC())
|
||||
}
|
||||
search = strings.TrimSpace(search)
|
||||
if search != "" {
|
||||
baseQuery += " AND (ip LIKE ? OR jail LIKE ? OR server_name LIKE ? OR COALESCE(hostname,'') LIKE ? OR COALESCE(country,'') LIKE ?)"
|
||||
pattern := "%" + search + "%"
|
||||
for i := 0; i < 5; i++ {
|
||||
args = append(args, pattern)
|
||||
}
|
||||
}
|
||||
if country != "" && country != "all" {
|
||||
if country == "__unknown__" {
|
||||
baseQuery += " AND (country IS NULL OR country = '')"
|
||||
} else {
|
||||
baseQuery += " AND LOWER(COALESCE(country,'')) = ?"
|
||||
args = append(args, strings.ToLower(country))
|
||||
}
|
||||
}
|
||||
|
||||
baseQuery += " ORDER BY occurred_at DESC LIMIT ? OFFSET ?"
|
||||
args = append(args, limit, offset)
|
||||
|
||||
rows, err := db.QueryContext(ctx, baseQuery, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var results []BanEventRecord
|
||||
for rows.Next() {
|
||||
var rec BanEventRecord
|
||||
var eventType sql.NullString
|
||||
if err := rows.Scan(
|
||||
&rec.ID,
|
||||
&rec.ServerID,
|
||||
&rec.ServerName,
|
||||
&rec.Jail,
|
||||
&rec.IP,
|
||||
&rec.Country,
|
||||
&rec.Hostname,
|
||||
&rec.Failures,
|
||||
&rec.Whois,
|
||||
&rec.Logs,
|
||||
&eventType,
|
||||
&rec.OccurredAt,
|
||||
&rec.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if eventType.Valid {
|
||||
rec.EventType = eventType.String
|
||||
} else {
|
||||
rec.EventType = "ban"
|
||||
}
|
||||
results = append(results, rec)
|
||||
}
|
||||
return results, rows.Err()
|
||||
}
|
||||
|
||||
// CountBanEventsFiltered returns the total count of ban events matching the same filters as ListBanEventsFiltered.
|
||||
func CountBanEventsFiltered(ctx context.Context, serverID string, since time.Time, search, country string) (int64, error) {
|
||||
if db == nil {
|
||||
return 0, errors.New("storage not initialised")
|
||||
}
|
||||
|
||||
query := `SELECT COUNT(*) FROM ban_events WHERE 1=1`
|
||||
args := []any{}
|
||||
|
||||
if serverID != "" {
|
||||
query += " AND server_id = ?"
|
||||
args = append(args, serverID)
|
||||
}
|
||||
if !since.IsZero() {
|
||||
query += " AND occurred_at >= ?"
|
||||
args = append(args, since.UTC())
|
||||
}
|
||||
search = strings.TrimSpace(search)
|
||||
if search != "" {
|
||||
query += " AND (ip LIKE ? OR jail LIKE ? OR server_name LIKE ? OR COALESCE(hostname,'') LIKE ? OR COALESCE(country,'') LIKE ?)"
|
||||
pattern := "%" + search + "%"
|
||||
for i := 0; i < 5; i++ {
|
||||
args = append(args, pattern)
|
||||
}
|
||||
}
|
||||
if country != "" && country != "all" {
|
||||
if country == "__unknown__" {
|
||||
query += " AND (country IS NULL OR country = '')"
|
||||
} else {
|
||||
query += " AND LOWER(COALESCE(country,'')) = ?"
|
||||
args = append(args, strings.ToLower(country))
|
||||
}
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := db.QueryRowContext(ctx, query, args...).Scan(&total); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// CountBanEventsByServer returns simple aggregation per server.
|
||||
func CountBanEventsByServer(ctx context.Context, since time.Time) (map[string]int64, error) {
|
||||
if db == nil {
|
||||
@@ -918,6 +1051,7 @@ CREATE TABLE IF NOT EXISTS ban_events (
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ban_events_server_id ON ban_events(server_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_ban_events_occurred_at ON ban_events(occurred_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_ban_events_ip ON ban_events(ip);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS permanent_blocks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
Reference in New Issue
Block a user