Improve ban events search search through all db-entries and also implement pagination to load more events

This commit is contained in:
2026-02-03 14:31:52 +01:00
parent b3e32fd5c1
commit 5f14da5934
13 changed files with 494 additions and 225 deletions

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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,