Skip to main content

jax_daemon/http_server/api/v0/bucket/
stat.rs

1use axum::extract::{Json, State};
2use axum::response::{IntoResponse, Response};
3use common::mount::PrincipalRole;
4use common::prelude::{Link, MountError};
5use reqwest::{Client, RequestBuilder, Url};
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9use crate::http_server::api::client::ApiRequest;
10use crate::ServiceState;
11
12#[derive(Debug, Clone, Serialize, Deserialize, clap::Args)]
13pub struct StatRequest {
14    /// Bucket ID to get stats for
15    #[arg(long)]
16    pub bucket_id: Uuid,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct StatResponse {
21    pub bucket_id: Uuid,
22    pub name: String,
23    pub height: u64,
24    pub link: Link,
25    pub published: bool,
26    pub peers: Vec<StatPeerInfo>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct StatPeerInfo {
31    pub public_key: String,
32    pub role: String,
33    pub is_self: bool,
34}
35
36pub async fn handler(
37    State(state): State<ServiceState>,
38    Json(req): Json<StatRequest>,
39) -> Result<impl IntoResponse, StatError> {
40    let mount = state.peer().mount_for_read(req.bucket_id).await?;
41    let inner = mount.inner().await;
42    let manifest = inner.manifest();
43
44    let self_key = state.peer().secret().public().to_hex();
45
46    let peers: Vec<StatPeerInfo> = manifest
47        .shares()
48        .iter()
49        .map(|(key_hex, share)| {
50            let role = match share.role() {
51                PrincipalRole::Owner => "Owner",
52                PrincipalRole::Mirror => "Mirror",
53            };
54            StatPeerInfo {
55                public_key: key_hex.clone(),
56                role: role.to_string(),
57                is_self: *key_hex == self_key,
58            }
59        })
60        .collect();
61
62    Ok((
63        http::StatusCode::OK,
64        Json(StatResponse {
65            bucket_id: req.bucket_id,
66            name: manifest.name().to_string(),
67            height: inner.height(),
68            link: mount.link().await,
69            published: manifest.is_published(),
70            peers,
71        }),
72    )
73        .into_response())
74}
75
76#[derive(Debug, thiserror::Error)]
77pub enum StatError {
78    #[error("Mount error: {0}")]
79    Mount(#[from] MountError),
80}
81
82impl IntoResponse for StatError {
83    fn into_response(self) -> Response {
84        (
85            http::StatusCode::INTERNAL_SERVER_ERROR,
86            format!("Error: {}", self),
87        )
88            .into_response()
89    }
90}
91
92impl ApiRequest for StatRequest {
93    type Response = StatResponse;
94
95    fn build_request(self, base_url: &Url, client: &Client) -> RequestBuilder {
96        let full_url = base_url.join("/api/v0/bucket/stat").unwrap();
97        client.post(full_url).json(&self)
98    }
99}