Skip to main content

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

1use axum::extract::{Json, State};
2use axum::response::{IntoResponse, Response};
3use reqwest::{Client, RequestBuilder, Url};
4use serde::{Deserialize, Serialize};
5use time::OffsetDateTime;
6use uuid::Uuid;
7
8use common::prelude::Link;
9
10use crate::http_server::api::client::ApiRequest;
11use crate::ServiceState;
12
13#[derive(Debug, Clone, Serialize, Deserialize, clap::Args)]
14pub struct ListRequest {
15    /// Optional prefix filter
16    #[serde(skip_serializing_if = "Option::is_none")]
17    #[arg(long)]
18    pub prefix: Option<String>,
19
20    /// Optional limit
21    #[serde(skip_serializing_if = "Option::is_none")]
22    #[arg(long)]
23    pub limit: Option<u32>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct ListResponse {
28    pub buckets: Vec<BucketInfo>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct BucketInfo {
33    pub bucket_id: Uuid,
34    pub name: String,
35    pub link: Link,
36    #[serde(with = "time::serde::rfc3339")]
37    pub created_at: OffsetDateTime,
38}
39
40pub async fn handler(
41    State(state): State<ServiceState>,
42    Json(req): Json<ListRequest>,
43) -> Result<impl IntoResponse, ListError> {
44    // Query buckets from bucket_log
45    let buckets = state
46        .database()
47        .list_buckets(req.prefix, req.limit)
48        .await
49        .map_err(|e| ListError::Database(e.to_string()))?;
50
51    // Convert to response format
52    let bucket_infos = buckets
53        .into_iter()
54        .map(|b| BucketInfo {
55            bucket_id: b.id,
56            name: b.name,
57            link: b.link,
58            created_at: b.created_at,
59        })
60        .collect();
61
62    Ok((
63        http::StatusCode::OK,
64        Json(ListResponse {
65            buckets: bucket_infos,
66        }),
67    )
68        .into_response())
69}
70
71#[derive(Debug, thiserror::Error)]
72pub enum ListError {
73    #[error("Database error: {0}")]
74    Database(String),
75}
76
77impl IntoResponse for ListError {
78    fn into_response(self) -> Response {
79        (
80            http::StatusCode::INTERNAL_SERVER_ERROR,
81            "unknown server error",
82        )
83            .into_response()
84    }
85}
86
87// Client implementation - builds request for this operation
88impl ApiRequest for ListRequest {
89    type Response = ListResponse;
90
91    fn build_request(self, base_url: &Url, client: &Client) -> RequestBuilder {
92        let full_url = base_url.join("/api/v0/bucket/list").unwrap();
93        client.post(full_url).json(&self)
94    }
95}