Ugh
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
use crate::modes::server::common::{
|
||||
ApiResponse, AppState, ItemContentQuery, ItemInfo, ItemInfoListResponse, ItemInfoResponse,
|
||||
ItemQuery, ListItemsQuery, MetadataResponse, TagsQuery,
|
||||
};
|
||||
use crate::services::async_item_service::AsyncItemService;
|
||||
use crate::services::error::CoreError;
|
||||
use axum::{
|
||||
extract::{Path, Query, State},
|
||||
http::{StatusCode, header},
|
||||
@@ -5,9 +11,6 @@ use axum::{
|
||||
};
|
||||
use log::{debug, warn};
|
||||
use std::collections::HashMap;
|
||||
use crate::services::async_item_service::AsyncItemService;
|
||||
use crate::services::error::CoreError;
|
||||
use crate::modes::server::common::{AppState, ApiResponse, ItemInfo, TagsQuery, ListItemsQuery, ItemInfoListResponse, ItemInfoResponse, MetadataResponse, ItemQuery, ItemContentQuery};
|
||||
|
||||
// Helper functions to replace the missing binary_detection module
|
||||
async fn check_binary_content_allowed(
|
||||
@@ -35,13 +38,17 @@ async fn is_content_binary(
|
||||
Ok(text_val == "false")
|
||||
} else {
|
||||
// If text metadata isn't set, we need to check the content using streaming approach
|
||||
match item_service.get_item_content_info_streaming(
|
||||
item_id,
|
||||
None
|
||||
).await {
|
||||
match item_service
|
||||
.get_item_content_info_streaming(item_id, None)
|
||||
.await
|
||||
{
|
||||
Ok((_, _, is_binary)) => Ok(is_binary),
|
||||
Err(e) => {
|
||||
log::warn!("Failed to get content info for binary check for item {}: {}", item_id, e);
|
||||
log::warn!(
|
||||
"Failed to get content info for binary check for item {}: {}",
|
||||
item_id,
|
||||
e
|
||||
);
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
@@ -63,7 +70,7 @@ impl ResponseBuilder {
|
||||
log::warn!("Failed to serialize response: {}", e);
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
|
||||
Response::builder()
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.header(header::CONTENT_LENGTH, json.len().to_string())
|
||||
@@ -73,7 +80,7 @@ impl ResponseBuilder {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub fn binary(content: &[u8], mime_type: &str) -> Result<Response, StatusCode> {
|
||||
Response::builder()
|
||||
.header(header::CONTENT_TYPE, mime_type)
|
||||
@@ -86,7 +93,6 @@ impl ResponseBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Helper function to get mime type from metadata
|
||||
fn get_mime_type(metadata: &HashMap<String, String>) -> String {
|
||||
metadata
|
||||
@@ -104,7 +110,7 @@ fn apply_offset_length(content: &[u8], offset: u64, length: u64) -> &[u8] {
|
||||
} else {
|
||||
content_len
|
||||
};
|
||||
|
||||
|
||||
if start < content_len {
|
||||
&content[start as usize..end as usize]
|
||||
} else {
|
||||
@@ -126,11 +132,11 @@ fn handle_item_error(error: CoreError) -> StatusCode {
|
||||
/// Helper function to create AsyncItemService from AppState
|
||||
fn create_item_service(state: &AppState) -> AsyncItemService {
|
||||
AsyncItemService::new(
|
||||
state.data_dir.clone(),
|
||||
state.db.clone(),
|
||||
state.data_dir.clone(),
|
||||
state.db.clone(),
|
||||
state.item_service.clone(),
|
||||
state.cmd.clone(),
|
||||
state.settings.clone()
|
||||
state.settings.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -185,13 +191,18 @@ pub async fn handle_list_items(
|
||||
// Apply pagination
|
||||
let start = params.start.unwrap_or(0) as usize;
|
||||
let count = params.count.unwrap_or(100) as usize;
|
||||
let items_with_meta: Vec<_> = items_with_meta.into_iter().skip(start).take(count).collect();
|
||||
let items_with_meta: Vec<_> = items_with_meta
|
||||
.into_iter()
|
||||
.skip(start)
|
||||
.take(count)
|
||||
.collect();
|
||||
|
||||
let item_infos: Vec<ItemInfo> = items_with_meta
|
||||
.into_iter()
|
||||
.map(|item_with_meta| {
|
||||
let item_id = item_with_meta.item.id.unwrap_or(0);
|
||||
let item_tags: Vec<String> = item_with_meta.tags.iter().map(|t| t.name.clone()).collect();
|
||||
let item_tags: Vec<String> =
|
||||
item_with_meta.tags.iter().map(|t| t.name.clone()).collect();
|
||||
let item_meta = item_with_meta.meta_as_map();
|
||||
|
||||
ItemInfo {
|
||||
@@ -239,7 +250,7 @@ async fn handle_as_meta_response_with_metadata(
|
||||
) -> Result<Response, StatusCode> {
|
||||
// Check if content is binary
|
||||
let is_binary = is_content_binary(item_service, item_id, metadata).await?;
|
||||
|
||||
|
||||
// Get the content if it's not binary
|
||||
if is_binary {
|
||||
// Return JSON with content as None and error message
|
||||
@@ -248,7 +259,7 @@ async fn handle_as_meta_response_with_metadata(
|
||||
"content": serde_json::Value::Null,
|
||||
"error": "Content is binary"
|
||||
});
|
||||
|
||||
|
||||
Response::builder()
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.status(StatusCode::UNPROCESSABLE_ENTITY)
|
||||
@@ -256,10 +267,7 @@ async fn handle_as_meta_response_with_metadata(
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
} else {
|
||||
// Get the content as text
|
||||
match item_service.get_item_content_info(
|
||||
item_id,
|
||||
None
|
||||
).await {
|
||||
match item_service.get_item_content_info(item_id, None).await {
|
||||
Ok((content, _, _)) => {
|
||||
// Apply offset and length
|
||||
let content_len = content.len() as u64;
|
||||
@@ -269,13 +277,13 @@ async fn handle_as_meta_response_with_metadata(
|
||||
} else {
|
||||
content_len
|
||||
};
|
||||
|
||||
|
||||
let response_content = if start < content_len {
|
||||
&content[start as usize..end as usize]
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
|
||||
|
||||
// Convert to UTF-8 string
|
||||
let content_str = match String::from_utf8(response_content.to_vec()) {
|
||||
Ok(s) => s,
|
||||
@@ -286,7 +294,7 @@ async fn handle_as_meta_response_with_metadata(
|
||||
"content": serde_json::Value::Null,
|
||||
"error": "Content is not valid UTF-8"
|
||||
});
|
||||
|
||||
|
||||
let response = Response::builder()
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.status(StatusCode::UNPROCESSABLE_ENTITY)
|
||||
@@ -295,14 +303,14 @@ async fn handle_as_meta_response_with_metadata(
|
||||
return Ok(response);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Return JSON with metadata and content
|
||||
let response_body = serde_json::json!({
|
||||
"metadata": metadata,
|
||||
"content": content_str,
|
||||
"error": serde_json::Value::Null
|
||||
});
|
||||
|
||||
|
||||
Response::builder()
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(axum::body::Body::from(response_body.to_string()))
|
||||
@@ -316,7 +324,6 @@ async fn handle_as_meta_response_with_metadata(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/api/item/",
|
||||
@@ -342,21 +349,19 @@ async fn handle_as_meta_response_with_metadata(
|
||||
pub async fn handle_post_item(
|
||||
State(_state): State<AppState>,
|
||||
) -> Result<Json<ApiResponse<ItemInfo>>, StatusCode> {
|
||||
|
||||
// This is a simplified implementation
|
||||
// In a real implementation, you'd need to properly parse multipart/form-data
|
||||
// or JSON payload with the item data
|
||||
|
||||
|
||||
let response = ApiResponse::<ItemInfo> {
|
||||
success: false,
|
||||
data: None,
|
||||
error: Some("POST /api/item/ not yet implemented".to_string()),
|
||||
};
|
||||
|
||||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/item/latest/content",
|
||||
@@ -397,9 +402,7 @@ pub async fn handle_get_item_latest_content(
|
||||
let item_service = create_item_service(&state);
|
||||
|
||||
// First find the item to get its ID and metadata
|
||||
let item_with_meta = item_service
|
||||
.find_item(vec![], tags, HashMap::new())
|
||||
.await;
|
||||
let item_with_meta = item_service.find_item(vec![], tags, HashMap::new()).await;
|
||||
|
||||
match item_with_meta {
|
||||
Ok(item) => {
|
||||
@@ -408,9 +411,26 @@ pub async fn handle_get_item_latest_content(
|
||||
// Handle as_meta parameter
|
||||
if params.as_meta {
|
||||
// Force stream=false and allow_binary=false for as_meta=true
|
||||
handle_as_meta_response_with_metadata(&item_service, item_id, &metadata, params.offset, params.length).await
|
||||
handle_as_meta_response_with_metadata(
|
||||
&item_service,
|
||||
item_id,
|
||||
&metadata,
|
||||
params.offset,
|
||||
params.length,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
stream_item_content_response_with_metadata(&item_service, item_id, &metadata, params.allow_binary, params.offset, params.length, params.stream, None).await
|
||||
stream_item_content_response_with_metadata(
|
||||
&item_service,
|
||||
item_id,
|
||||
&metadata,
|
||||
params.allow_binary,
|
||||
params.offset,
|
||||
params.length,
|
||||
params.stream,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
Err(CoreError::ItemNotFoundGeneric) => Err(StatusCode::NOT_FOUND),
|
||||
@@ -421,7 +441,6 @@ pub async fn handle_get_item_latest_content(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/item/{item_id}/content",
|
||||
@@ -459,8 +478,10 @@ pub async fn handle_get_item_content(
|
||||
return Err(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
debug!("ITEM_API: Getting content for item {} with stream={}, allow_binary={}, offset={}, length={}",
|
||||
item_id, params.stream, params.allow_binary, params.offset, params.length);
|
||||
debug!(
|
||||
"ITEM_API: Getting content for item {} with stream={}, allow_binary={}, offset={}, length={}",
|
||||
item_id, params.stream, params.allow_binary, params.offset, params.length
|
||||
);
|
||||
|
||||
let filter = build_filter_string(¶ms);
|
||||
|
||||
@@ -468,15 +489,31 @@ pub async fn handle_get_item_content(
|
||||
// Handle as_meta parameter
|
||||
if params.as_meta {
|
||||
// Force stream=false and allow_binary=false for as_meta=true
|
||||
let result = handle_as_meta_response(&item_service, item_id, params.offset, params.length).await;
|
||||
let result =
|
||||
handle_as_meta_response(&item_service, item_id, params.offset, params.length).await;
|
||||
if let Ok(response) = &result {
|
||||
debug!("ITEM_API: Response content-length: {:?}", response.headers().get("content-length"));
|
||||
debug!(
|
||||
"ITEM_API: Response content-length: {:?}",
|
||||
response.headers().get("content-length")
|
||||
);
|
||||
}
|
||||
result
|
||||
} else {
|
||||
let result = stream_item_content_response(&item_service, item_id, params.allow_binary, params.offset, params.length, params.stream, filter).await;
|
||||
let result = stream_item_content_response(
|
||||
&item_service,
|
||||
item_id,
|
||||
params.allow_binary,
|
||||
params.offset,
|
||||
params.length,
|
||||
params.stream,
|
||||
filter,
|
||||
)
|
||||
.await;
|
||||
if let Ok(response) = &result {
|
||||
debug!("ITEM_API: Response content-length: {:?}", response.headers().get("content-length"));
|
||||
debug!(
|
||||
"ITEM_API: Response content-length: {:?}",
|
||||
response.headers().get("content-length")
|
||||
);
|
||||
}
|
||||
result
|
||||
}
|
||||
@@ -499,7 +536,17 @@ async fn stream_item_content_response(
|
||||
})?;
|
||||
|
||||
let metadata = item_with_meta.meta_as_map();
|
||||
stream_item_content_response_with_metadata(item_service, item_id, &metadata, allow_binary, offset, length, stream, filter).await
|
||||
stream_item_content_response_with_metadata(
|
||||
item_service,
|
||||
item_id,
|
||||
&metadata,
|
||||
allow_binary,
|
||||
offset,
|
||||
length,
|
||||
stream,
|
||||
filter,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn stream_item_content_response_with_metadata(
|
||||
@@ -512,22 +559,23 @@ async fn stream_item_content_response_with_metadata(
|
||||
stream: bool,
|
||||
filter: Option<String>,
|
||||
) -> Result<Response, StatusCode> {
|
||||
debug!("STREAM_ITEM_CONTENT_RESPONSE_WITH_METADATA: stream={}", stream);
|
||||
debug!(
|
||||
"STREAM_ITEM_CONTENT_RESPONSE_WITH_METADATA: stream={}",
|
||||
stream
|
||||
);
|
||||
let mime_type = get_mime_type(metadata);
|
||||
|
||||
|
||||
// Check if content is binary when allow_binary is false
|
||||
check_binary_content_allowed(item_service, item_id, metadata, allow_binary).await?;
|
||||
|
||||
|
||||
if stream {
|
||||
debug!("STREAMING: Using streaming approach");
|
||||
match item_service.stream_item_content_by_id_with_metadata(
|
||||
item_id,
|
||||
metadata,
|
||||
true,
|
||||
offset,
|
||||
length,
|
||||
filter
|
||||
).await {
|
||||
match item_service
|
||||
.stream_item_content_by_id_with_metadata(
|
||||
item_id, metadata, true, offset, length, filter,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok((stream, _)) => {
|
||||
let body = axum::body::Body::from_stream(stream);
|
||||
let response = Response::builder()
|
||||
@@ -543,16 +591,16 @@ async fn stream_item_content_response_with_metadata(
|
||||
}
|
||||
} else {
|
||||
debug!("NON-STREAMING: Building full response in memory");
|
||||
match item_service.get_item_content_info(
|
||||
item_id,
|
||||
filter
|
||||
).await {
|
||||
match item_service.get_item_content_info(item_id, filter).await {
|
||||
Ok((content, _, _)) => {
|
||||
let response_content = apply_offset_length(&content, offset, length);
|
||||
|
||||
debug!("NON-STREAMING: Content length: {}, response length: {}",
|
||||
content.len(), response_content.len());
|
||||
|
||||
|
||||
debug!(
|
||||
"NON-STREAMING: Content length: {}, response length: {}",
|
||||
content.len(),
|
||||
response_content.len()
|
||||
);
|
||||
|
||||
ResponseBuilder::binary(response_content, &mime_type)
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -563,8 +611,6 @@ async fn stream_item_content_response_with_metadata(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/item/latest/meta",
|
||||
@@ -655,4 +701,3 @@ pub async fn handle_get_item_meta(
|
||||
Err(e) => Err(handle_item_error(e)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user