1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3
4/// HTTP client for communicating with the onis appview API.
5#[derive(Clone)]
6pub struct AppviewClient {
7 /// Underlying HTTP client.
8 client: reqwest::Client,
9 /// Base URL of the appview, e.g. `"http://localhost:3000"`.
10 base_url: String,
11}
12
13/// A zone that is due for re-verification, returned by the appview stale zones endpoint.
14#[derive(Debug, Deserialize)]
15pub struct StaleZoneEntry {
16 /// Domain name of the zone.
17 pub zone: String,
18 /// DID of the zone owner.
19 pub did: String,
20 /// Current verification status.
21 pub verified: bool,
22 /// Unix timestamp when the zone was first seen.
23 pub first_seen: i64,
24 /// Unix timestamp of the last successful verification, if any.
25 pub last_verified: Option<i64>,
26}
27
28/// Wire format for the `GET /v1/zones/stale` response.
29#[derive(Debug, Deserialize)]
30struct StaleZonesResponse {
31 /// List of zones needing re-verification.
32 zones: Vec<StaleZoneEntry>,
33}
34
35/// Request body for `PUT /v1/verification`.
36#[derive(Serialize)]
37struct VerificationBody {
38 /// Domain name of the zone.
39 zone: String,
40 /// DID of the zone owner.
41 did: String,
42 /// Whether the zone passed delegation verification.
43 verified: bool,
44}
45
46impl AppviewClient {
47 pub fn new(base_url: String) -> Self {
48 Self {
49 client: reqwest::Client::new(),
50 base_url,
51 }
52 }
53
54 pub async fn get_stale_zones(&self, checked_before: i64) -> Result<Vec<StaleZoneEntry>> {
55 let url = format!(
56 "{}/v1/zones/stale?checked_before={}",
57 self.base_url, checked_before
58 );
59
60 let body: StaleZonesResponse = self
61 .client
62 .get(&url)
63 .send()
64 .await
65 .context("failed to fetch stale zones")?
66 .error_for_status()
67 .context("stale zones request failed")?
68 .json()
69 .await
70 .context("failed to deserialize stale zones response")?;
71
72 Ok(body.zones)
73 }
74
75 pub async fn set_verification(&self, zone: &str, did: &str, verified: bool) -> Result<()> {
76 let url = format!("{}/v1/verification", self.base_url);
77
78 self.client
79 .put(&url)
80 .json(&VerificationBody {
81 zone: zone.to_string(),
82 did: did.to_string(),
83 verified,
84 })
85 .send()
86 .await
87 .context("failed to set verification")?
88 .error_for_status()
89 .with_context(|| format!("set verification for {zone} failed"))?;
90
91 Ok(())
92 }
93}