adguardian/fetch/
fetch_query_log.rs1use base64::{engine::general_purpose::STANDARD, Engine as _};
2use reqwest::header::{HeaderValue, AUTHORIZATION, CONTENT_LENGTH};
3use serde::Deserialize;
4
5#[derive(Default, Deserialize)]
6#[serde(default)]
7pub struct QueryResponse {
8 pub data: Vec<Query>,
9}
10
11#[derive(Default, Deserialize)]
12#[serde(default)]
13pub struct Query {
14 pub cached: bool,
15 pub client: String,
16 pub upstream: String,
17 #[serde(rename = "elapsedMs")]
18 pub elapsed_ms: String,
19 pub question: Question,
20 pub reason: String,
21 pub time: String,
22}
23
24#[derive(Default, Deserialize)]
25#[serde(default)]
26pub struct Question {
27 pub class: String,
28 pub name: String,
29 #[serde(rename = "type")]
30 pub question_type: String,
31}
32
33pub async fn fetch_adguard_query_log(
34 client: &reqwest::Client,
35 endpoint: &str,
36 username: &str,
37 password: &str,
38 limit: u32,
39) -> Result<QueryResponse, anyhow::Error> {
40 let auth_string = format!("{}:{}", username, password);
41 let auth_header_value = format!("Basic {}", STANDARD.encode(&auth_string));
42 let mut headers = reqwest::header::HeaderMap::new();
43 headers.insert(AUTHORIZATION, auth_header_value.parse()?);
44 headers.insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
45
46 let url = format!("{}/control/querylog?limit={}", endpoint, limit);
47 let response = client.get(&url).headers(headers).send().await?;
48 if !response.status().is_success() {
49 return Err(anyhow::anyhow!(
50 "Request failed with status code {}",
51 response.status()
52 ));
53 }
54
55 let data = response.json().await?;
56 Ok(data)
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use crate::fetch::fetch_stats::StatsResponse;
63 use crate::fetch::fetch_status::StatusResponse;
64
65 #[test]
67 fn empty_and_partial_json_decode_to_defaults() {
68 serde_json::from_str::<QueryResponse>("{}").unwrap();
69 serde_json::from_str::<StatsResponse>("{}").unwrap();
70 serde_json::from_str::<StatusResponse>("{}").unwrap();
71 serde_json::from_str::<StatsResponse>(r#"{"num_dns_queries":5}"#).unwrap();
72
73 let q = r#"{"cached":false,"client":"1.2.3.4","elapsedMs":"0.1",
75 "question":{"class":"IN","name":"x.com","type":"A"},"reason":"x","time":"t"}"#;
76 assert_eq!(serde_json::from_str::<Query>(q).unwrap().upstream, "");
77 }
78}