Skip to main content

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

1use axum::extract::{Json, State};
2use axum::response::{IntoResponse, Response};
3use common::prelude::MountError;
4use reqwest::{Client, RequestBuilder, Url};
5use serde::{Deserialize, Serialize};
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 PublishRequest {
13    /// Bucket ID to publish
14    #[arg(long)]
15    pub bucket_id: Uuid,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct PublishResponse {
20    pub bucket_id: Uuid,
21    pub published: bool,
22    pub new_bucket_link: String,
23}
24
25pub async fn handler(
26    State(state): State<ServiceState>,
27    Json(req): Json<PublishRequest>,
28) -> Result<impl IntoResponse, PublishError> {
29    tracing::info!("PUBLISH API: Publishing bucket {}", req.bucket_id);
30
31    // Load mount at current head
32    let mount = state.peer().mount(req.bucket_id).await?;
33
34    // Check if already published
35    if mount.is_published().await {
36        tracing::info!("PUBLISH API: Bucket {} is already published", req.bucket_id);
37        // Still return success, just note it's already published
38    }
39
40    // Save mount with publish=true (grants decryption to all mirrors)
41    // This saves to blobs, appends to log, and notifies peers
42    let new_bucket_link = state.peer().save_mount(&mount, true).await?;
43
44    tracing::info!(
45        "PUBLISH API: Bucket {} published, new link: {}",
46        req.bucket_id,
47        new_bucket_link.hash()
48    );
49
50    Ok((
51        http::StatusCode::OK,
52        Json(PublishResponse {
53            bucket_id: req.bucket_id,
54            published: true,
55            new_bucket_link: new_bucket_link.hash().to_string(),
56        }),
57    )
58        .into_response())
59}
60
61#[derive(Debug, thiserror::Error)]
62pub enum PublishError {
63    #[error("Mount error: {0}")]
64    Mount(#[from] MountError),
65}
66
67impl IntoResponse for PublishError {
68    fn into_response(self) -> Response {
69        match self {
70            PublishError::Mount(_) => (
71                http::StatusCode::INTERNAL_SERVER_ERROR,
72                "Unexpected error".to_string(),
73            )
74                .into_response(),
75        }
76    }
77}
78
79// Client implementation - builds request for this operation
80impl ApiRequest for PublishRequest {
81    type Response = PublishResponse;
82
83    fn build_request(self, base_url: &Url, client: &Client) -> RequestBuilder {
84        let full_url = base_url.join("/api/v0/bucket/publish").unwrap();
85        client.post(full_url).json(&self)
86    }
87}