adguardian/fetch/
fetch_stats.rs1use base64::{engine::general_purpose::STANDARD, Engine as _};
2use reqwest::header::{HeaderValue, AUTHORIZATION, CONTENT_LENGTH};
6use serde::Deserialize;
7use std::collections::HashMap;
8
9#[derive(Debug, Deserialize, Clone)]
10pub struct DomainData {
11 pub name: String,
12 pub count: i32,
13}
14
15#[derive(Debug, Default, Deserialize, Clone)]
16#[serde(default)]
17pub struct StatsResponse {
18 pub num_dns_queries: u64,
19 pub num_blocked_filtering: u64,
20 pub num_replaced_safebrowsing: u64,
21 pub num_replaced_safesearch: u64,
22 pub num_replaced_parental: u64,
23 pub avg_processing_time: f64,
24 pub time_units: String,
25 pub dns_queries: Vec<u64>,
26 pub blocked_filtering: Vec<u64>,
27 pub replaced_safebrowsing: Vec<u64>,
28 pub replaced_parental: Vec<u64>,
29
30 #[serde(default, skip_deserializing)]
31 pub dns_queries_chart: Vec<(f64, f64)>,
32 #[serde(default, skip_deserializing)]
33 pub blocked_filtering_chart: Vec<(f64, f64)>,
34
35 #[serde(
36 rename = "top_queried_domains",
37 deserialize_with = "deserialize_domains"
38 )]
39 pub top_queried_domains: Vec<DomainData>,
40 #[serde(
41 rename = "top_blocked_domains",
42 deserialize_with = "deserialize_domains"
43 )]
44 pub top_blocked_domains: Vec<DomainData>,
45 #[serde(rename = "top_clients", deserialize_with = "deserialize_domains")]
46 pub top_clients: Vec<DomainData>,
47}
48
49pub async fn fetch_adguard_stats(
50 client: &reqwest::Client,
51 endpoint: &str,
52 username: &str,
53 password: &str,
54) -> Result<StatsResponse, anyhow::Error> {
55 let auth_string = format!("{}:{}", username, password);
56 let auth_header_value = format!("Basic {}", STANDARD.encode(&auth_string));
57 let mut headers = reqwest::header::HeaderMap::new();
58 headers.insert(AUTHORIZATION, auth_header_value.parse()?);
59 headers.insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
60
61 let url = format!("{}/control/stats", endpoint);
62 let response = client.get(&url).headers(headers).send().await?;
63 if !response.status().is_success() {
64 return Err(anyhow::anyhow!(
65 "Request failed with status code {}",
66 response.status()
67 ));
68 }
69
70 let data = response.json().await?;
71 Ok(data)
72}
73
74fn deserialize_domains<'de, D>(deserializer: D) -> Result<Vec<DomainData>, D::Error>
76where
77 D: serde::Deserializer<'de>,
78{
79 let raw_vec: Vec<HashMap<String, i32>> = serde::Deserialize::deserialize(deserializer)?;
80 Ok(
81 raw_vec
82 .into_iter()
83 .flat_map(|mut map| {
84 map
85 .drain()
86 .map(|(name, count)| DomainData { name, count })
87 .collect::<Vec<_>>()
88 })
89 .collect(),
90 )
91}