mirror of
https://github.com/glanceapp/glance.git
synced 2024-12-14 17:13:11 +03:00
141 lines
4.2 KiB
Go
141 lines
4.2 KiB
Go
package feed
|
|
|
|
import (
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type piholeStatsResponse struct {
|
|
TotalQueries int `json:"dns_queries_today"`
|
|
QueriesSeries map[int64]int `json:"domains_over_time"`
|
|
BlockedQueries int `json:"ads_blocked_today"`
|
|
BlockedSeries map[int64]int `json:"ads_over_time"`
|
|
BlockedPercentage float64 `json:"ads_percentage_today"`
|
|
TopBlockedDomains map[string]int `json:"top_ads"`
|
|
DomainsBlocked int `json:"domains_being_blocked"`
|
|
}
|
|
|
|
// If user has some level of privacy enabled on Pihole, `json:"top_ads"` is an empty array
|
|
// Use alternate struct without that field to avoid error when unmarshalling
|
|
type piholeStatsResponsePrivate struct {
|
|
TotalQueries int `json:"dns_queries_today"`
|
|
QueriesSeries map[int64]int `json:"domains_over_time"`
|
|
BlockedQueries int `json:"ads_blocked_today"`
|
|
BlockedSeries map[int64]int `json:"ads_over_time"`
|
|
BlockedPercentage float64 `json:"ads_percentage_today"`
|
|
DomainsBlocked int `json:"domains_being_blocked"`
|
|
}
|
|
|
|
func FetchPiholeStats(instanceURL, token string) (*DNSStats, error) {
|
|
if token == "" {
|
|
return nil, errors.New("missing API token")
|
|
}
|
|
|
|
requestURL := strings.TrimRight(instanceURL, "/") +
|
|
"/admin/api.php?summaryRaw&topItems&overTimeData10mins&auth=" + token
|
|
|
|
request, err := http.NewRequest("GET", requestURL, nil)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
responseJson, err := decodeJsonFromRequest[piholeStatsResponse](defaultClient, request)
|
|
|
|
if err != nil {
|
|
// Refer to piholeStatsResponsePrivate above
|
|
responseJsonPriv, err :=
|
|
decodeJsonFromRequest[piholeStatsResponsePrivate](defaultClient, request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Copy the results back to responseJson, leaving the TopBlockedDomains field empty
|
|
responseJson.TotalQueries = responseJsonPriv.TotalQueries
|
|
responseJson.QueriesSeries = responseJsonPriv.QueriesSeries
|
|
responseJson.BlockedQueries = responseJsonPriv.BlockedQueries
|
|
responseJson.BlockedSeries = responseJsonPriv.BlockedSeries
|
|
responseJson.BlockedPercentage = responseJsonPriv.BlockedPercentage
|
|
responseJson.TopBlockedDomains = make(map[string]int)
|
|
responseJson.DomainsBlocked = responseJsonPriv.DomainsBlocked
|
|
}
|
|
|
|
stats := &DNSStats{
|
|
TotalQueries: responseJson.TotalQueries,
|
|
BlockedQueries: responseJson.BlockedQueries,
|
|
BlockedPercent: int(responseJson.BlockedPercentage),
|
|
DomainsBlocked: responseJson.DomainsBlocked,
|
|
}
|
|
|
|
if len(responseJson.TopBlockedDomains) > 0 {
|
|
domains := make([]DNSStatsBlockedDomain, 0, len(responseJson.TopBlockedDomains))
|
|
|
|
for domain, count := range responseJson.TopBlockedDomains {
|
|
domains = append(domains, DNSStatsBlockedDomain{
|
|
Domain: domain,
|
|
PercentBlocked: int(float64(count) / float64(responseJson.BlockedQueries) * 100),
|
|
})
|
|
}
|
|
|
|
sort.Slice(domains, func(a, b int) bool {
|
|
return domains[a].PercentBlocked > domains[b].PercentBlocked
|
|
})
|
|
|
|
stats.TopBlockedDomains = domains[:min(len(domains), 5)]
|
|
}
|
|
|
|
// Pihole _should_ return data for the last 24 hours in a 10 minute interval, 6*24 = 144
|
|
if len(responseJson.QueriesSeries) != 144 || len(responseJson.BlockedSeries) != 144 {
|
|
slog.Warn(
|
|
"DNS stats for pihole: did not get expected 144 data points",
|
|
"len(queries)", len(responseJson.QueriesSeries),
|
|
"len(blocked)", len(responseJson.BlockedSeries),
|
|
)
|
|
return stats, nil
|
|
}
|
|
|
|
var lowestTimestamp int64 = 0
|
|
|
|
for timestamp := range responseJson.QueriesSeries {
|
|
if lowestTimestamp == 0 || timestamp < lowestTimestamp {
|
|
lowestTimestamp = timestamp
|
|
}
|
|
}
|
|
|
|
maxQueriesInSeries := 0
|
|
|
|
for i := 0; i < 8; i++ {
|
|
queries := 0
|
|
blocked := 0
|
|
|
|
for j := 0; j < 18; j++ {
|
|
index := lowestTimestamp + int64(i*10800+j*600)
|
|
|
|
queries += responseJson.QueriesSeries[index]
|
|
blocked += responseJson.BlockedSeries[index]
|
|
}
|
|
|
|
if queries > maxQueriesInSeries {
|
|
maxQueriesInSeries = queries
|
|
}
|
|
|
|
stats.Series[i] = DNSStatsSeries{
|
|
Queries: queries,
|
|
Blocked: blocked,
|
|
}
|
|
|
|
if queries > 0 {
|
|
stats.Series[i].PercentBlocked = int(float64(blocked) / float64(queries) * 100)
|
|
}
|
|
}
|
|
|
|
for i := 0; i < 8; i++ {
|
|
stats.Series[i].PercentTotal = int(float64(stats.Series[i].Queries) / float64(maxQueriesInSeries) * 100)
|
|
}
|
|
|
|
return stats, nil
|
|
}
|