From 8198b65f29f83e2ff51d4c0f89a7b7dcbe9c2fc1 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 7 Sep 2018 17:49:33 +0300 Subject: [PATCH 1/5] API /stats_top -- show only top entries for last 3 minutes --- control.go | 8 ++++++++ helpers.go | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/control.go b/control.go index 70cb9349..2552df53 100644 --- a/control.go +++ b/control.go @@ -409,6 +409,9 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) { domains := map[string]int{} blocked := map[string]int{} clients := map[string]int{} + now := time.Now() + timeWindow := time.Minute * 3 + notBefore := now.Add(timeWindow * -1) for _, value := range values { entry, ok := value.(map[string]interface{}) @@ -419,6 +422,11 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) { host := getHost(entry) reason := getReason(entry) client := getClient(entry) + time := getTime(entry) + if time.Before(notBefore) { + // skip if the entry is before specified cutoff + continue + } if len(host) > 0 { domains[host]++ } diff --git a/helpers.go b/helpers.go index a460f98f..d39c5e93 100644 --- a/helpers.go +++ b/helpers.go @@ -7,6 +7,7 @@ import ( "net/http" "sort" "strings" + "time" ) func clamp(value, low, high int) int { @@ -167,6 +168,22 @@ func getClient(entry map[string]interface{}) string { return client } +func getTime(entry map[string]interface{}) time.Time { + t, ok := entry["time"] + if !ok { + return time.Time{} + } + tstr, ok := t.(string) + if !ok { + return time.Time{} + } + value, err := time.Parse(time.RFC3339, tstr) + if err != nil { + return time.Time{} + } + return value +} + // ------------------------------------------------- // helper functions for parsing parameters from body // ------------------------------------------------- From f623c3d9093d5a953336ee6a2e74507070a7def8 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 7 Sep 2018 17:50:03 +0300 Subject: [PATCH 2/5] API /stats_top -- sort top entries by value --- control.go | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/control.go b/control.go index 2552df53..8161c954 100644 --- a/control.go +++ b/control.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -438,21 +439,35 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) { } } - toMarshal := map[string]interface{}{ - "top_queried_domains": produceTop(domains, 50), - "top_blocked_domains": produceTop(blocked, 50), - "top_clients": produceTop(clients, 50), - } - json, err := json.Marshal(toMarshal) - if err != nil { - errortext := fmt.Sprintf("Couldn't marshal into JSON: %s", err) - log.Println(errortext) - http.Error(w, errortext, http.StatusBadGateway) - return + // use manual json marshalling because we want maps to be sorted by value + json := bytes.Buffer{} + json.WriteString("{\n") + + gen := func(json *bytes.Buffer, name string, top map[string]int, addComma bool) { + json.WriteString(" \"") + json.WriteString(name) + json.WriteString("\": {\n") + sorted := sortByValue(top) + for i, key := range sorted { + fmt.Fprintf(json, " \"%s\": %d", key, top[key])) + if i+1 != len(sorted) { + json.WriteByte(',') + } + json.WriteByte('\n') + } + json.WriteString(" }") + if addComma { + json.WriteByte(',') + } + json.WriteByte('\n') } + gen(&json, "top_queried_domains", domains, true) + gen(&json, "top_blocked_domains", blocked, true) + gen(&json, "top_clients", clients, false) + json.WriteString("}\n") w.Header().Set("Content-Type", "application/json") - _, err = w.Write(json) + _, err = w.Write(json.Bytes()) if err != nil { errortext := fmt.Sprintf("Couldn't write body: %s", err) log.Println(errortext) From 7094ed4f28e25d0610cbe0f68dd721b80e737442 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 7 Sep 2018 17:59:24 +0300 Subject: [PATCH 3/5] Fixup of previous commit -- errand keystroke crept in --- control.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control.go b/control.go index 8161c954..643458ba 100644 --- a/control.go +++ b/control.go @@ -449,7 +449,7 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) { json.WriteString("\": {\n") sorted := sortByValue(top) for i, key := range sorted { - fmt.Fprintf(json, " \"%s\": %d", key, top[key])) + fmt.Fprintf(json, " \"%s\": %d", key, top[key]) if i+1 != len(sorted) { json.WriteByte(',') } From 4ba8293c06891fae66e487d170cd7aa01eb45e93 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 7 Sep 2018 18:04:18 +0300 Subject: [PATCH 4/5] web interface -- change text from 'general counters' to 'general statistics' --- client/src/components/Dashboard/Counters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/Dashboard/Counters.js b/client/src/components/Dashboard/Counters.js index c9fba7c2..094b78ed 100644 --- a/client/src/components/Dashboard/Counters.js +++ b/client/src/components/Dashboard/Counters.js @@ -5,7 +5,7 @@ import Card from '../ui/Card'; import Tooltip from '../ui/Tooltip'; const Counters = props => ( - + From 31893410892bd047c9f6ea8f602717e6996c9491 Mon Sep 17 00:00:00 2001 From: Eugene Bujak Date: Fri, 7 Sep 2018 18:04:31 +0300 Subject: [PATCH 5/5] web interface -- Make refresh buttons reload all data, not just counters --- client/src/components/Dashboard/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/Dashboard/index.js b/client/src/components/Dashboard/index.js index 885b72f4..035ff9bc 100644 --- a/client/src/components/Dashboard/index.js +++ b/client/src/components/Dashboard/index.js @@ -27,8 +27,8 @@ class Dashboard extends Component { dashboard.processingTopStats; const disableButton = ; - const refreshFullButton = ; - const refreshButton = ; + const refreshFullButton = ; + const refreshButton = ; return (