From 243e77fba435030b274f942fb408824d98525079 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Wed, 13 Aug 2025 13:25:27 -0300 Subject: [PATCH] feat: add optional allow_binary parameter to item handlers Co-authored-by: aider (openai/andrew/openrouter/anthropic/claude-sonnet-4) --- src/modes/server/api/item.rs | 19 +++++++++++-------- src/modes/server/common.rs | 8 ++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/modes/server/api/item.rs b/src/modes/server/api/item.rs index 19260fe..f133eca 100644 --- a/src/modes/server/api/item.rs +++ b/src/modes/server/api/item.rs @@ -13,7 +13,7 @@ use anyhow::{Result, anyhow}; use crate::compression_engine::{CompressionType, get_compression_engine}; use crate::db; -use crate::modes::server::common::{AppState, ApiResponse, ItemInfo, ItemContentInfo, TagsQuery, ListItemsQuery}; +use crate::modes::server::common::{AppState, ApiResponse, ItemInfo, ItemContentInfo, TagsQuery, ListItemsQuery, ItemQuery}; use crate::common::is_binary::is_binary; #[utoipa::path( @@ -211,7 +211,8 @@ pub async fn handle_delete_item( (status = 500, description = "Internal server error - Failed to retrieve item content") ), params( - ("tags" = Option, Query, description = "Comma-separated list of tags to filter by (e.g., 'important,work'). If specified, returns the latest item matching ALL tags.") + ("tags" = Option, Query, description = "Comma-separated list of tags to filter by (e.g., 'important,work'). If specified, returns the latest item matching ALL tags."), + ("allow_binary" = Option, Query, description = "Whether to include content for binary files (default: false). When false, binary files will only return metadata.") ), security( ("bearerAuth" = []) @@ -240,7 +241,7 @@ pub async fn handle_get_item_latest( }; if let Some(item) = item { - match get_item_content_info(&item, &state.data_dir, &mut *conn).await { + match get_item_content_info(&item, &state.data_dir, &mut *conn, params.allow_binary).await { Ok(content_info) => { let response = ApiResponse { success: true, @@ -275,7 +276,8 @@ pub async fn handle_get_item_latest( (status = 500, description = "Internal server error - Failed to retrieve item content") ), params( - ("item_id" = i64, Path, description = "Unique identifier of the item to retrieve (must be positive)") + ("item_id" = i64, Path, description = "Unique identifier of the item to retrieve (must be positive)"), + ("allow_binary" = Option, Query, description = "Whether to include content for binary files (default: false). When false, binary files will only return metadata.") ), security( ("bearerAuth" = []) @@ -285,6 +287,7 @@ pub async fn handle_get_item_latest( pub async fn handle_get_item( State(state): State, Path(item_id): Path, + Query(params): Query, ) -> Result>, StatusCode> { // Validate that item ID is positive to prevent path traversal issues if item_id <= 0 { @@ -297,7 +300,7 @@ pub async fn handle_get_item( warn!("Failed to get item {} for content: {}", item_id, e); StatusCode::INTERNAL_SERVER_ERROR })? { - match get_item_content_info(&item, &state.data_dir, &mut *conn).await { + match get_item_content_info(&item, &state.data_dir, &mut *conn, params.allow_binary).await { Ok(content_info) => { let response = ApiResponse { success: true, @@ -453,7 +456,7 @@ async fn get_item_content(item: &db::Item, data_dir: &PathBuf) -> Result Ok(content) } -async fn get_item_content_info(item: &db::Item, data_dir: &PathBuf, conn: &mut rusqlite::Connection) -> Result { +async fn get_item_content_info(item: &db::Item, data_dir: &PathBuf, conn: &mut rusqlite::Connection, allow_binary: bool) -> Result { let item_id = item.id.ok_or_else(|| anyhow!("Item missing ID"))?; // Validate that item ID is positive to prevent path traversal issues @@ -487,8 +490,8 @@ async fn get_item_content_info(item: &db::Item, data_dir: &PathBuf, conn: &mut r is_binary(&buffer[..bytes_read]) }; - // Get content if not binary - let content = if is_binary { + // Get content if not binary or if binary is allowed + let content = if is_binary && !allow_binary { None } else { match get_item_content(item, data_dir).await { diff --git a/src/modes/server/common.rs b/src/modes/server/common.rs index cd51286..6e92abb 100644 --- a/src/modes/server/common.rs +++ b/src/modes/server/common.rs @@ -69,6 +69,8 @@ pub struct ItemContentInfo { #[derive(Debug, Deserialize)] pub struct TagsQuery { pub tags: Option, + #[serde(default)] + pub allow_binary: bool, } #[derive(Debug, Deserialize)] @@ -79,6 +81,12 @@ pub struct ListItemsQuery { pub count: Option, } +#[derive(Debug, Deserialize)] +pub struct ItemQuery { + #[serde(default)] + pub allow_binary: bool, +} + fn check_bearer_auth(auth_str: &str, expected_password: &str) -> bool { auth_str.starts_with("Bearer ") && &auth_str[7..] == expected_password }