You've already forked caddy-opnsense-blocker
Refine the dashboard and requests log UX
This commit is contained in:
@@ -236,6 +236,16 @@ func knownBotExistsClause(ipExpression string) string {
|
||||
)`
|
||||
}
|
||||
|
||||
func anyBotExistsClause(ipExpression string) string {
|
||||
return `EXISTS (
|
||||
SELECT 1
|
||||
FROM ip_investigations i
|
||||
WHERE i.ip = ` + ipExpression + `
|
||||
AND json_valid(i.payload_json)
|
||||
AND json_type(i.payload_json, '$.bot') IS NOT NULL
|
||||
)`
|
||||
}
|
||||
|
||||
func overviewFilterQueryParts(options model.OverviewOptions) (joins []string, clauses []string) {
|
||||
if !options.ShowAllowed {
|
||||
joins = append(joins, `LEFT JOIN ip_state s ON s.ip = e.client_ip`)
|
||||
@@ -443,11 +453,27 @@ func (s *Store) GetOverview(ctx context.Context, since time.Time, limit int, opt
|
||||
if err != nil {
|
||||
return model.Overview{}, err
|
||||
}
|
||||
topIPsByEvents, err := s.listTopIPRows(ctx, since, limit, "events", options)
|
||||
topIPsByEvents, err := s.listTopIPRows(ctx, since, limit, "events", options, "all")
|
||||
if err != nil {
|
||||
return model.Overview{}, err
|
||||
}
|
||||
topIPsByTraffic, err := s.listTopIPRows(ctx, since, limit, "traffic", options)
|
||||
topBotIPsByEvents, err := s.listTopIPRows(ctx, since, limit, "events", options, "bots")
|
||||
if err != nil {
|
||||
return model.Overview{}, err
|
||||
}
|
||||
topNonBotIPsByEvents, err := s.listTopIPRows(ctx, since, limit, "events", options, "non-bots")
|
||||
if err != nil {
|
||||
return model.Overview{}, err
|
||||
}
|
||||
topIPsByTraffic, err := s.listTopIPRows(ctx, since, limit, "traffic", options, "all")
|
||||
if err != nil {
|
||||
return model.Overview{}, err
|
||||
}
|
||||
topBotIPsByTraffic, err := s.listTopIPRows(ctx, since, limit, "traffic", options, "bots")
|
||||
if err != nil {
|
||||
return model.Overview{}, err
|
||||
}
|
||||
topNonBotIPsByTraffic, err := s.listTopIPRows(ctx, since, limit, "traffic", options, "non-bots")
|
||||
if err != nil {
|
||||
return model.Overview{}, err
|
||||
}
|
||||
@@ -477,17 +503,27 @@ func (s *Store) GetOverview(ctx context.Context, since time.Time, limit int, opt
|
||||
overview.Methods = methods
|
||||
overview.Bots = bots
|
||||
overview.TopIPsByEvents = topIPsByEvents
|
||||
overview.TopBotIPsByEvents = topBotIPsByEvents
|
||||
overview.TopNonBotIPsByEvents = topNonBotIPsByEvents
|
||||
overview.TopIPsByTraffic = topIPsByTraffic
|
||||
overview.TopBotIPsByTraffic = topBotIPsByTraffic
|
||||
overview.TopNonBotIPsByTraffic = topNonBotIPsByTraffic
|
||||
overview.TopSources = topSources
|
||||
overview.TopURLs = topURLs
|
||||
return overview, nil
|
||||
}
|
||||
|
||||
func (s *Store) listTopIPRows(ctx context.Context, since time.Time, limit int, orderBy string, options model.OverviewOptions) ([]model.TopIPRow, error) {
|
||||
func (s *Store) listTopIPRows(ctx context.Context, since time.Time, limit int, orderBy string, options model.OverviewOptions, botScope string) ([]model.TopIPRow, error) {
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
joins, clauses := overviewFilterQueryParts(options)
|
||||
switch botScope {
|
||||
case "bots":
|
||||
clauses = append(clauses, anyBotExistsClause(`e.client_ip`))
|
||||
case "non-bots":
|
||||
clauses = append(clauses, `NOT `+anyBotExistsClause(`e.client_ip`))
|
||||
}
|
||||
query := fmt.Sprintf(`
|
||||
SELECT e.client_ip,
|
||||
COUNT(*) AS event_count,
|
||||
@@ -797,6 +833,9 @@ func (s *Store) ListEvents(ctx context.Context, since time.Time, limit int, opti
|
||||
if limit <= 0 {
|
||||
limit = 100
|
||||
}
|
||||
if options.Offset < 0 {
|
||||
options.Offset = 0
|
||||
}
|
||||
joins, clauses := eventFilterQueryParts(options)
|
||||
query := `
|
||||
SELECT e.id, e.source_name, e.profile_name, e.occurred_at, e.remote_ip, e.client_ip, e.host,
|
||||
@@ -815,8 +854,8 @@ func (s *Store) ListEvents(ctx context.Context, since time.Time, limit int, opti
|
||||
if len(clauses) > 0 {
|
||||
query += ` WHERE ` + strings.Join(clauses, ` AND `)
|
||||
}
|
||||
query += ` ORDER BY e.occurred_at DESC, e.id DESC LIMIT ?`
|
||||
args = append(args, limit)
|
||||
query += ` ORDER BY e.occurred_at DESC, e.id DESC LIMIT ? OFFSET ?`
|
||||
args = append(args, limit, options.Offset)
|
||||
|
||||
rows, err := s.db.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
|
||||
@@ -109,6 +109,12 @@ func TestStoreRecordsEventsAndState(t *testing.T) {
|
||||
if len(overview.TopIPsByEvents) != 1 || overview.TopIPsByEvents[0].IP != event.ClientIP {
|
||||
t.Fatalf("unexpected top ips by events: %+v", overview.TopIPsByEvents)
|
||||
}
|
||||
if len(overview.TopBotIPsByEvents) != 0 {
|
||||
t.Fatalf("expected no bot top ips by events before investigation, got %+v", overview.TopBotIPsByEvents)
|
||||
}
|
||||
if len(overview.TopNonBotIPsByEvents) != 1 || overview.TopNonBotIPsByEvents[0].IP != event.ClientIP {
|
||||
t.Fatalf("unexpected top non-bot ips by events: %+v", overview.TopNonBotIPsByEvents)
|
||||
}
|
||||
if len(overview.TopSources) != 1 || overview.TopSources[0].SourceName != event.SourceName {
|
||||
t.Fatalf("unexpected top sources: %+v", overview.TopSources)
|
||||
}
|
||||
@@ -252,6 +258,9 @@ func TestStoreOverviewLeaderboardsUseTrafficFromRawJSON(t *testing.T) {
|
||||
if overview.TopIPsByEvents[0].IP != "203.0.113.10" || overview.TopIPsByEvents[0].Events != 2 || overview.TopIPsByEvents[0].TrafficBytes != 3072 {
|
||||
t.Fatalf("unexpected top IP by events row: %+v", overview.TopIPsByEvents[0])
|
||||
}
|
||||
if len(overview.TopNonBotIPsByEvents) < 2 || overview.TopNonBotIPsByEvents[0].IP != "203.0.113.10" || overview.TopNonBotIPsByEvents[0].Events != 2 {
|
||||
t.Fatalf("unexpected top non-bot IP by events rows: %+v", overview.TopNonBotIPsByEvents)
|
||||
}
|
||||
if len(overview.TopIPsByTraffic) < 2 {
|
||||
t.Fatalf("expected at least 2 top IP rows by traffic, got %+v", overview.TopIPsByTraffic)
|
||||
}
|
||||
@@ -272,6 +281,22 @@ func TestStoreOverviewLeaderboardsUseTrafficFromRawJSON(t *testing.T) {
|
||||
}); err != nil {
|
||||
t.Fatalf("save top bot investigation: %v", err)
|
||||
}
|
||||
refreshedOverview, err := db.GetOverview(ctx, baseTime.Add(-time.Minute), 10, model.OverviewOptions{ShowKnownBots: true, ShowAllowed: true})
|
||||
if err != nil {
|
||||
t.Fatalf("get refreshed overview: %v", err)
|
||||
}
|
||||
if len(refreshedOverview.TopBotIPsByEvents) == 0 || refreshedOverview.TopBotIPsByEvents[0].IP != "203.0.113.10" {
|
||||
t.Fatalf("unexpected top bot IPs by events after investigation: %+v", refreshedOverview.TopBotIPsByEvents)
|
||||
}
|
||||
if len(refreshedOverview.TopNonBotIPsByEvents) == 0 || refreshedOverview.TopNonBotIPsByEvents[0].IP != "203.0.113.20" {
|
||||
t.Fatalf("unexpected top non-bot IPs by events after investigation: %+v", refreshedOverview.TopNonBotIPsByEvents)
|
||||
}
|
||||
if len(refreshedOverview.TopBotIPsByTraffic) == 0 || refreshedOverview.TopBotIPsByTraffic[0].IP != "203.0.113.10" {
|
||||
t.Fatalf("unexpected top bot IPs by traffic after investigation: %+v", refreshedOverview.TopBotIPsByTraffic)
|
||||
}
|
||||
if len(refreshedOverview.TopNonBotIPsByTraffic) == 0 || refreshedOverview.TopNonBotIPsByTraffic[0].IP != "203.0.113.20" {
|
||||
t.Fatalf("unexpected top non-bot IPs by traffic after investigation: %+v", refreshedOverview.TopNonBotIPsByTraffic)
|
||||
}
|
||||
if _, err := db.SetManualOverride(ctx, "203.0.113.20", model.ManualOverrideForceAllow, model.IPStateAllowed, "manual allow"); err != nil {
|
||||
t.Fatalf("set manual override for filter test: %v", err)
|
||||
}
|
||||
@@ -283,9 +308,21 @@ func TestStoreOverviewLeaderboardsUseTrafficFromRawJSON(t *testing.T) {
|
||||
if len(filtered.TopIPsByEvents) != 0 {
|
||||
t.Fatalf("expected filtered top IPs by events to be empty, got %+v", filtered.TopIPsByEvents)
|
||||
}
|
||||
if len(filtered.TopBotIPsByEvents) != 0 {
|
||||
t.Fatalf("expected filtered top bot IPs by events to be empty, got %+v", filtered.TopBotIPsByEvents)
|
||||
}
|
||||
if len(filtered.TopNonBotIPsByEvents) != 0 {
|
||||
t.Fatalf("expected filtered top non-bot IPs by events to be empty, got %+v", filtered.TopNonBotIPsByEvents)
|
||||
}
|
||||
if len(filtered.TopIPsByTraffic) != 0 {
|
||||
t.Fatalf("expected filtered top IPs by traffic to be empty, got %+v", filtered.TopIPsByTraffic)
|
||||
}
|
||||
if len(filtered.TopBotIPsByTraffic) != 0 {
|
||||
t.Fatalf("expected filtered top bot IPs by traffic to be empty, got %+v", filtered.TopBotIPsByTraffic)
|
||||
}
|
||||
if len(filtered.TopNonBotIPsByTraffic) != 0 {
|
||||
t.Fatalf("expected filtered top non-bot IPs by traffic to be empty, got %+v", filtered.TopNonBotIPsByTraffic)
|
||||
}
|
||||
if len(filtered.TopSources) != 0 {
|
||||
t.Fatalf("expected filtered top sources to be empty, got %+v", filtered.TopSources)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user