Skip to main content

jax_daemon/http_server/api/v0/bucket/
history.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 crate::http_server::api::client::ApiRequest;
9use crate::ServiceState;
10
11#[derive(Debug, Clone, Serialize, Deserialize, clap::Args)]
12pub struct HistoryRequest {
13    /// Bucket ID to get history for
14    #[arg(long)]
15    pub bucket_id: Uuid,
16
17    /// Page number (0-indexed)
18    #[serde(default)]
19    #[arg(long)]
20    pub page: Option<u32>,
21
22    /// Number of entries per page (default 50)
23    #[serde(default)]
24    #[arg(long)]
25    pub page_size: Option<u32>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct HistoryResponse {
30    pub bucket_id: Uuid,
31    pub entries: Vec<HistoryEntry>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct HistoryEntry {
36    pub link_hash: String,
37    pub height: u64,
38    pub published: bool,
39    #[serde(with = "time::serde::rfc3339")]
40    pub created_at: OffsetDateTime,
41}
42
43pub async fn handler(
44    State(state): State<ServiceState>,
45    Json(req): Json<HistoryRequest>,
46) -> Result<impl IntoResponse, HistoryError> {
47    let page = req.page.unwrap_or(0);
48    let page_size = req.page_size.unwrap_or(50);
49
50    let entries = state
51        .database()
52        .get_bucket_logs(&req.bucket_id, page, page_size)
53        .await
54        .map_err(|e| HistoryError::Database(e.to_string()))?;
55
56    let history_entries: Vec<HistoryEntry> = entries
57        .into_iter()
58        .map(|e| HistoryEntry {
59            link_hash: e.current_link.to_string(),
60            height: e.height,
61            published: e.published,
62            created_at: e.created_at,
63        })
64        .collect();
65
66    Ok((
67        http::StatusCode::OK,
68        Json(HistoryResponse {
69            bucket_id: req.bucket_id,
70            entries: history_entries,
71        }),
72    )
73        .into_response())
74}
75
76#[derive(Debug, thiserror::Error)]
77pub enum HistoryError {
78    #[error("Database error: {0}")]
79    Database(String),
80}
81
82impl IntoResponse for HistoryError {
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 HistoryRequest {
93    type Response = HistoryResponse;
94
95    fn build_request(self, base_url: &Url, client: &Client) -> RequestBuilder {
96        let full_url = base_url.join("/api/v0/bucket/history").unwrap();
97        client.post(full_url).json(&self)
98    }
99}