use axum::{ extract::{Query, State}, http::{StatusCode, header}, response::{IntoResponse, Response}, }; use pile_config::Label; use pile_value::value::AsyncReader; use serde::Deserialize; use std::{sync::Arc, time::Instant}; use tracing::debug; use utoipa::ToSchema; use crate::Datasets; #[derive(Deserialize, ToSchema)] pub struct ItemQuery { source: String, key: String, } /// Fetch the raw bytes of an item by source and key #[utoipa::path( get, path = "/item", params( ("source" = String, Query, description = "Source label"), ("key" = String, Query, description = "Item key"), ), responses( (status = 200, description = "Raw item bytes"), (status = 400, description = "Invalid source label"), (status = 404, description = "Item not found"), (status = 500, description = "Internal server error"), ) )] pub async fn item_get( State(state): State>, Query(params): Query, ) -> Response { let start = Instant::now(); debug!( message = "Serving /item", source = params.source, key = params.key ); let label = match Label::try_from(params.source.clone()) { Ok(l) => l, Err(e) => return (StatusCode::BAD_REQUEST, format!("{e:?}")).into_response(), }; let Some(item) = state.get(&label, ¶ms.key).await else { return StatusCode::NOT_FOUND.into_response(); }; let mime = item.mime().to_string(); let mut reader = match item.read().await { Ok(r) => r, Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")).into_response(), }; debug!( message = "Served /item", source = params.source, key = params.key, time_ms = start.elapsed().as_millis() ); match reader.read_to_end().await { Ok(bytes) => (StatusCode::OK, [(header::CONTENT_TYPE, mime)], bytes).into_response(), Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")).into_response(), } }