jax_daemon/http_server/api/v0/bucket/
stat.rs1use 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 #[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}