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 => ( - + 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 ( diff --git a/control.go b/control.go index 70cb9349..643458ba 100644 --- a/control.go +++ b/control.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -409,6 +410,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 +423,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]++ } @@ -430,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) 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 // -------------------------------------------------