From ed06a4681efbc1ef424e3f1cba00e2c278015c47 Mon Sep 17 00:00:00 2001 From: nazeh Date: Thu, 17 Oct 2024 18:28:22 +0300 Subject: [PATCH] feat: add head endpoint --- Cargo.lock | 4 +-- pubky-common/Cargo.toml | 2 +- pubky-homeserver/src/routes.rs | 3 +- pubky-homeserver/src/routes/public.rs | 47 ++++++++++++++++++++++++++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be81384..63a8c5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1704,9 +1704,9 @@ dependencies = [ [[package]] name = "pubky-timestamp" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d018cd110531645acddc6493bb9f07d1f4869205d53ceea69f2331d5ff7af" +checksum = "084b6e5bfcc186781b71257d636b660f20e94bb588c3ba52393fd9faf7a7bfda" dependencies = [ "base32", "document-features", diff --git a/pubky-common/Cargo.toml b/pubky-common/Cargo.toml index 48104cd..1220dff 100644 --- a/pubky-common/Cargo.toml +++ b/pubky-common/Cargo.toml @@ -18,7 +18,7 @@ crypto_secretbox = { version = "0.1.1", features = ["std"] } argon2 = { version = "0.5.3", features = ["std"] } serde = { workspace = true, optional = true } -pubky-timestamp = { version = "0.1.0", features = ["full"] } +pubky-timestamp = { version = "0.2.0", features = ["full"] } [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = "0.3.69" diff --git a/pubky-homeserver/src/routes.rs b/pubky-homeserver/src/routes.rs index 2105e86..6404ffb 100644 --- a/pubky-homeserver/src/routes.rs +++ b/pubky-homeserver/src/routes.rs @@ -1,6 +1,6 @@ use axum::{ extract::DefaultBodyLimit, - routing::{delete, get, post, put}, + routing::{delete, get, head, post, put}, Router, }; use tower_cookies::CookieManagerLayer; @@ -25,6 +25,7 @@ fn base(state: AppState) -> Router { .route("/:pubky/session", delete(auth::signout)) .route("/:pubky/*path", put(public::put)) .route("/:pubky/*path", get(public::get)) + .route("/:pubky/*path", head(public::head)) .route("/:pubky/*path", delete(public::delete)) .route("/events/", get(feed::feed)) .layer(CookieManagerLayer::new()) diff --git a/pubky-homeserver/src/routes/public.rs b/pubky-homeserver/src/routes/public.rs index bd61e23..14c3957 100644 --- a/pubky-homeserver/src/routes/public.rs +++ b/pubky-homeserver/src/routes/public.rs @@ -1,7 +1,7 @@ use axum::{ body::Body, extract::State, - http::{header, Response, StatusCode}, + http::{header, HeaderMap, HeaderValue, Response, StatusCode}, response::IntoResponse, }; use futures_util::stream::StreamExt; @@ -125,6 +125,26 @@ pub async fn get( } } +pub async fn head( + State(state): State, + pubky: Pubky, + path: EntryPath, +) -> Result { + verify(path.as_str())?; + + let rtxn = state.db.env.read_txn()?; + + match state + .db + .get_entry(&rtxn, pubky.public_key(), path.as_str())? + .as_ref() + .map(HeaderMap::from) + { + Some(headers) => Ok(headers), + None => Err(Error::with_status(StatusCode::NOT_FOUND)), + } +} + pub async fn delete( State(mut state): State, pubky: Pubky, @@ -188,3 +208,28 @@ fn verify(path: &str) -> Result<()> { Ok(()) } + +impl From<&Entry> for HeaderMap { + fn from(entry: &Entry) -> Self { + let mut headers = HeaderMap::new(); + headers.insert(header::CONTENT_LENGTH, entry.content_length().into()); + headers.insert(header::LAST_MODIFIED, entry.timestamp().format_http_date()); + headers.insert( + header::CONTENT_TYPE, + // TODO: when setting content type from user input, we should validate it as a HeaderValue + entry + .content_type() + .try_into() + .or(HeaderValue::from_str("")) + .expect("valid header value"), + ); + headers.insert( + header::ETAG, + format!("\"{}\"", entry.content_hash()) + .try_into() + .expect("hex string is valid"), + ); + + headers + } +}