package main import ( "fmt" "sort" ) type TrafficMapCity struct { TrafficCityLocation Country string `json:"country"` Requests int64 `json:"requests"` UniqueIPs int64 `json:"uniqueIps"` Alerts int64 `json:"alerts"` Decisions int64 `json:"decisions"` LastSeen int64 `json:"lastSeen"` } func (s *Store) trafficMapCities(where string, args []any) ([]TrafficMapCity, bool, error) { rows, err := s.db.Query(`SELECT ip,country,SUM(kind='request'),SUM(kind='alert'),SUM(kind='decision'),MAX(ts) FROM traffic_events`+where+` GROUP BY ip,country`, args...) if err != nil { return nil, false, err } defer rows.Close() groups := map[string]*TrafficMapCity{} for rows.Next() { var ip, country string var requests, alerts, decisions, last int64 if err = rows.Scan(&ip, &country, &requests, &alerts, &decisions, &last); err != nil { return nil, false, err } g := lookupTrafficCityLocation(ip) if !g.Located { continue } key := fmt.Sprintf("%s/%s/%s/%.5f/%.5f", country, g.Region, g.City, g.Latitude, g.Longitude) c := groups[key] if c == nil { c = &TrafficMapCity{TrafficCityLocation: g, Country: country} groups[key] = c } c.Requests += requests c.Alerts += alerts c.Decisions += decisions if requests > 0 { c.UniqueIPs++ } if last > c.LastSeen { c.LastSeen = last } } if err = rows.Err(); err != nil { return nil, false, err } result := make([]TrafficMapCity, 0, len(groups)) for _, c := range groups { result = append(result, *c) } sort.Slice(result, func(i, j int) bool { a, b := result[i], result[j] x, y := a.Requests+a.Alerts+a.Decisions, b.Requests+b.Alerts+b.Decisions if x != y { return x > y } return fmt.Sprint(a.Country, a.Region, a.City, a.Latitude, a.Longitude) < fmt.Sprint(b.Country, b.Region, b.City, b.Latitude, b.Longitude) }) limited := len(result) > 500 if limited { result = result[:500] } return result, limited, nil }