diff --git a/Cargo.toml b/Cargo.toml index 880021f..855ca0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ unimplemented = "deny" unwrap_used = "warn" expect_used = "warn" type_complexity = "allow" +len_without_is_empty = "allow" # # MARK: dependencies diff --git a/crates/pile-dataset/src/serve/field.rs b/crates/pile-dataset/src/serve/field.rs index ef561f8..9e2e6df 100644 --- a/crates/pile-dataset/src/serve/field.rs +++ b/crates/pile-dataset/src/serve/field.rs @@ -18,6 +18,8 @@ pub struct FieldQuery { source: String, key: String, path: String, + #[serde(default)] + download: bool, } /// Extract a specific field from an item's metadata @@ -79,21 +81,38 @@ pub async fn get_field( time_ms = start.elapsed().as_millis() ); + let disposition = if params.download { + "attachment" + } else { + "inline" + }; + match value { PileValue::String(s) => ( StatusCode::OK, - [(header::CONTENT_TYPE, "text/plain")], + [ + (header::CONTENT_TYPE, "text/plain".to_owned()), + (header::CONTENT_DISPOSITION, disposition.to_owned()), + ], s.to_string(), ) .into_response(), PileValue::Blob { mime, bytes } => ( StatusCode::OK, - [(header::CONTENT_TYPE, mime.to_string())], + [ + (header::CONTENT_TYPE, mime.to_string()), + (header::CONTENT_DISPOSITION, disposition.to_owned()), + ], bytes.as_ref().clone(), ) .into_response(), _ => match value.to_json(&state).await { - Ok(json) => (StatusCode::OK, Json(json)).into_response(), + Ok(json) => ( + StatusCode::OK, + [(header::CONTENT_DISPOSITION, disposition.to_owned())], + Json(json), + ) + .into_response(), Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")).into_response(), }, } diff --git a/crates/pile-dataset/src/serve/item.rs b/crates/pile-dataset/src/serve/item.rs index 1505dfd..6797245 100644 --- a/crates/pile-dataset/src/serve/item.rs +++ b/crates/pile-dataset/src/serve/item.rs @@ -19,6 +19,8 @@ use crate::Datasets; pub struct ItemQuery { source: String, key: String, + #[serde(default)] + download: bool, } /// Parse a `Range: bytes=...` header value. @@ -161,11 +163,18 @@ pub async fn item_get( StatusCode::OK }; + let disposition = if params.download { + "attachment" + } else { + "inline" + }; + let mut builder = axum::http::Response::builder() .status(status) .header(header::CONTENT_TYPE, mime) .header(header::ACCEPT_RANGES, "bytes") - .header(header::CONTENT_LENGTH, length); + .header(header::CONTENT_LENGTH, length) + .header(header::CONTENT_DISPOSITION, disposition); if is_range { builder = builder.header(