mirror of
https://github.com/glanceapp/glance.git
synced 2025-02-23 05:40:52 +01:00
Add custom unmarshalling for pihole queries stats
+ hide-graph and hide-top-domains options for DNS stats widget
This commit is contained in:
parent
e524dd111e
commit
ac7f3805d4
@ -1358,6 +1358,8 @@ Preview:
|
|||||||
| username | string | when service is `adguard` | |
|
| username | string | when service is `adguard` | |
|
||||||
| password | string | when service is `adguard` | |
|
| password | string | when service is `adguard` | |
|
||||||
| token | string | when service is `pihole` | |
|
| token | string | when service is `pihole` | |
|
||||||
|
| hide-graph | bool | no | false |
|
||||||
|
| hide-top-domains | bool | no | false |
|
||||||
| hour-format | string | no | 12h |
|
| hour-format | string | no | 12h |
|
||||||
|
|
||||||
##### `service`
|
##### `service`
|
||||||
@ -1378,6 +1380,12 @@ Only required when using AdGuard Home. The password used to log into the admin d
|
|||||||
##### `token`
|
##### `token`
|
||||||
Only required when using Pi-hole. The API token which can be found in `Settings -> API -> Show API token`. Can be specified from an environment variable using the syntax `${VARIABLE_NAME}`.
|
Only required when using Pi-hole. The API token which can be found in `Settings -> API -> Show API token`. Can be specified from an environment variable using the syntax `${VARIABLE_NAME}`.
|
||||||
|
|
||||||
|
##### `hide-graph`
|
||||||
|
Whether to hide the graph showing the number of queries over time.
|
||||||
|
|
||||||
|
##### `hide-top-domains`
|
||||||
|
Whether to hide the list of top blocked domains.
|
||||||
|
|
||||||
##### `hour-format`
|
##### `hour-format`
|
||||||
Whether to display the relative time in the graph in `12h` or `24h` format.
|
Whether to display the relative time in the graph in `12h` or `24h` format.
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{ $showGraph := not (or .HideGraph (eq (len .Stats.Series) 0)) }}
|
||||||
|
{{ if $showGraph }}
|
||||||
<div class="dns-stats-graph margin-top-15">
|
<div class="dns-stats-graph margin-top-15">
|
||||||
<div class="dns-stats-graph-gridlines-container">
|
<div class="dns-stats-graph-gridlines-container">
|
||||||
<svg class="dns-stats-graph-gridlines" shape-rendering="crispEdges" viewBox="0 0 1 100" preserveAspectRatio="none">
|
<svg class="dns-stats-graph-gridlines" shape-rendering="crispEdges" viewBox="0 0 1 100" preserveAspectRatio="none">
|
||||||
@ -67,9 +69,10 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{ if .Stats.TopBlockedDomains }}
|
{{ if and (not .HideTopDomains) .Stats.TopBlockedDomains }}
|
||||||
<details class="details margin-top-40">
|
<details class="details {{ if $showGraph }}margin-top-40{{ else }}margin-top-15{{ end }}">
|
||||||
<summary class="summary">Top blocked domains</summary>
|
<summary class="summary">Top blocked domains</summary>
|
||||||
<ul class="list list-gap-4 list-with-transition size-h5">
|
<ul class="list list-gap-4 list-with-transition size-h5">
|
||||||
{{ range .Stats.TopBlockedDomains }}
|
{{ range .Stats.TopBlockedDomains }}
|
||||||
|
@ -21,6 +21,8 @@ type dnsStatsWidget struct {
|
|||||||
Stats *dnsStats `yaml:"-"`
|
Stats *dnsStats `yaml:"-"`
|
||||||
|
|
||||||
HourFormat string `yaml:"hour-format"`
|
HourFormat string `yaml:"hour-format"`
|
||||||
|
HideGraph bool `yaml:"hide-graph"`
|
||||||
|
HideTopDomains bool `yaml:"hide-top-domains"`
|
||||||
Service string `yaml:"service"`
|
Service string `yaml:"service"`
|
||||||
AllowInsecure bool `yaml:"allow-insecure"`
|
AllowInsecure bool `yaml:"allow-insecure"`
|
||||||
URL string `yaml:"url"`
|
URL string `yaml:"url"`
|
||||||
@ -58,9 +60,9 @@ func (widget *dnsStatsWidget) update(ctx context.Context) {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
if widget.Service == "adguard" {
|
if widget.Service == "adguard" {
|
||||||
stats, err = fetchAdguardStats(string(widget.URL), widget.AllowInsecure, string(widget.Username), string(widget.Password))
|
stats, err = fetchAdguardStats(widget.URL, widget.AllowInsecure, widget.Username, widget.Password, widget.HideGraph)
|
||||||
} else {
|
} else {
|
||||||
stats, err = fetchPiholeStats(string(widget.URL), widget.AllowInsecure, string(widget.Token))
|
stats, err = fetchPiholeStats(widget.URL, widget.AllowInsecure, widget.Token, widget.HideGraph)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !widget.canContinueUpdateAfterHandlingErr(err) {
|
if !widget.canContinueUpdateAfterHandlingErr(err) {
|
||||||
@ -111,7 +113,7 @@ type adguardStatsResponse struct {
|
|||||||
TopBlockedDomains []map[string]int `json:"top_blocked_domains"`
|
TopBlockedDomains []map[string]int `json:"top_blocked_domains"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchAdguardStats(instanceURL string, allowInsecure bool, username, password string) (*dnsStats, error) {
|
func fetchAdguardStats(instanceURL string, allowInsecure bool, username, password string, noGraph bool) (*dnsStats, error) {
|
||||||
requestURL := strings.TrimRight(instanceURL, "/") + "/control/stats"
|
requestURL := strings.TrimRight(instanceURL, "/") + "/control/stats"
|
||||||
|
|
||||||
request, err := http.NewRequest("GET", requestURL, nil)
|
request, err := http.NewRequest("GET", requestURL, nil)
|
||||||
@ -170,6 +172,10 @@ func fetchAdguardStats(instanceURL string, allowInsecure bool, username, passwor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if noGraph {
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|
||||||
queriesSeries := responseJson.QueriesSeries
|
queriesSeries := responseJson.QueriesSeries
|
||||||
blockedSeries := responseJson.BlockedSeries
|
blockedSeries := responseJson.BlockedSeries
|
||||||
|
|
||||||
@ -223,7 +229,7 @@ func fetchAdguardStats(instanceURL string, allowInsecure bool, username, passwor
|
|||||||
|
|
||||||
type piholeStatsResponse struct {
|
type piholeStatsResponse struct {
|
||||||
TotalQueries int `json:"dns_queries_today"`
|
TotalQueries int `json:"dns_queries_today"`
|
||||||
QueriesSeries map[int64]int `json:"domains_over_time"`
|
QueriesSeries piholeQueriesSeries `json:"domains_over_time"`
|
||||||
BlockedQueries int `json:"ads_blocked_today"`
|
BlockedQueries int `json:"ads_blocked_today"`
|
||||||
BlockedSeries map[int64]int `json:"ads_over_time"`
|
BlockedSeries map[int64]int `json:"ads_over_time"`
|
||||||
BlockedPercentage float64 `json:"ads_percentage_today"`
|
BlockedPercentage float64 `json:"ads_percentage_today"`
|
||||||
@ -231,6 +237,25 @@ type piholeStatsResponse struct {
|
|||||||
DomainsBlocked int `json:"domains_being_blocked"`
|
DomainsBlocked int `json:"domains_being_blocked"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the user has query logging disabled it's possible for domains_over_time to be returned as an
|
||||||
|
// empty array rather than a map which will prevent unmashalling the rest of the data so we use
|
||||||
|
// custom unmarshal behavior to fallback to an empty map.
|
||||||
|
// See https://github.com/glanceapp/glance/issues/289
|
||||||
|
type piholeQueriesSeries map[int64]int
|
||||||
|
|
||||||
|
func (p *piholeQueriesSeries) UnmarshalJSON(data []byte) error {
|
||||||
|
temp := make(map[int64]int)
|
||||||
|
|
||||||
|
err := json.Unmarshal(data, &temp)
|
||||||
|
if err != nil {
|
||||||
|
*p = make(piholeQueriesSeries)
|
||||||
|
} else {
|
||||||
|
*p = temp
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// If user has some level of privacy enabled on Pihole, `json:"top_ads"` is an empty array
|
// If user has some level of privacy enabled on Pihole, `json:"top_ads"` is an empty array
|
||||||
// Use custom unmarshal behavior to avoid not getting the rest of the valid data when unmarshalling
|
// Use custom unmarshal behavior to avoid not getting the rest of the valid data when unmarshalling
|
||||||
type piholeTopBlockedDomains map[string]int
|
type piholeTopBlockedDomains map[string]int
|
||||||
@ -250,7 +275,7 @@ func (p *piholeTopBlockedDomains) UnmarshalJSON(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchPiholeStats(instanceURL string, allowInsecure bool, token string) (*dnsStats, error) {
|
func fetchPiholeStats(instanceURL string, allowInsecure bool, token string, noGraph bool) (*dnsStats, error) {
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return nil, errors.New("missing API token")
|
return nil, errors.New("missing API token")
|
||||||
}
|
}
|
||||||
@ -299,6 +324,10 @@ func fetchPiholeStats(instanceURL string, allowInsecure bool, token string) (*dn
|
|||||||
stats.TopBlockedDomains = domains[:min(len(domains), 5)]
|
stats.TopBlockedDomains = domains[:min(len(domains), 5)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if noGraph {
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Pihole _should_ return data for the last 24 hours in a 10 minute interval, 6*24 = 144
|
// 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 {
|
if len(responseJson.QueriesSeries) != 144 || len(responseJson.BlockedSeries) != 144 {
|
||||||
slog.Warn(
|
slog.Warn(
|
||||||
|
Loading…
Reference in New Issue
Block a user