|
|
|
@@ -13,25 +13,27 @@ use anyhow::{Result, anyhow};
|
|
|
|
|
|
|
|
|
|
|
|
use crate::compression_engine::{CompressionType, get_compression_engine};
|
|
|
|
use crate::compression_engine::{CompressionType, get_compression_engine};
|
|
|
|
use crate::db;
|
|
|
|
use crate::db;
|
|
|
|
use crate::modes::server::common::{AppState, ApiResponse, ItemInfo, TagsQuery, ListItemsQuery};
|
|
|
|
use crate::modes::server::common::{AppState, ApiResponse, ItemInfo, ItemContentInfo, TagsQuery, ListItemsQuery};
|
|
|
|
|
|
|
|
use crate::common::is_binary::is_binary;
|
|
|
|
|
|
|
|
|
|
|
|
#[utoipa::path(
|
|
|
|
#[utoipa::path(
|
|
|
|
get,
|
|
|
|
get,
|
|
|
|
path = "/api/item/",
|
|
|
|
path = "/api/item/",
|
|
|
|
responses(
|
|
|
|
responses(
|
|
|
|
(status = 200, description = "Successfully retrieved list of items", body = ApiResponse<Vec<ItemInfo>>),
|
|
|
|
(status = 200, description = "Successfully retrieved paginated list of items with metadata and tags", body = ApiResponse<Vec<ItemInfo>>),
|
|
|
|
(status = 401, description = "Unauthorized"),
|
|
|
|
(status = 401, description = "Unauthorized - Invalid or missing authentication credentials"),
|
|
|
|
(status = 500, description = "Internal server error")
|
|
|
|
(status = 500, description = "Internal server error - Failed to retrieve items from database")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
params(
|
|
|
|
params(
|
|
|
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by"),
|
|
|
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by (e.g., 'important,work')"),
|
|
|
|
("order" = Option<String>, Query, description = "Sort order (newest or oldest)"),
|
|
|
|
("order" = Option<String>, Query, description = "Sort order: 'newest' (default) or 'oldest'"),
|
|
|
|
("start" = Option<u64>, Query, description = "Starting index for pagination"),
|
|
|
|
("start" = Option<u64>, Query, description = "Starting index for pagination (default: 0)"),
|
|
|
|
("count" = Option<u64>, Query, description = "Number of items to return")
|
|
|
|
("count" = Option<u64>, Query, description = "Maximum number of items to return (default: 100, max: 1000)")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
security(
|
|
|
|
security(
|
|
|
|
("bearerAuth" = [])
|
|
|
|
("bearerAuth" = [])
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
tag = "item"
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
pub async fn handle_list_items(
|
|
|
|
pub async fn handle_list_items(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
State(state): State<AppState>,
|
|
|
|
@@ -121,13 +123,20 @@ pub async fn handle_list_items(
|
|
|
|
post,
|
|
|
|
post,
|
|
|
|
path = "/api/item/",
|
|
|
|
path = "/api/item/",
|
|
|
|
responses(
|
|
|
|
responses(
|
|
|
|
(status = 200, description = "Successfully created item", body = ApiResponse<ItemInfo>),
|
|
|
|
(status = 201, description = "Successfully created new item", body = ApiResponse<ItemInfo>),
|
|
|
|
(status = 401, description = "Unauthorized"),
|
|
|
|
(status = 400, description = "Bad request - Invalid input data"),
|
|
|
|
(status = 500, description = "Internal server error")
|
|
|
|
(status = 401, description = "Unauthorized - Invalid or missing authentication credentials"),
|
|
|
|
|
|
|
|
(status = 500, description = "Internal server error - Failed to create item")
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
request_body(
|
|
|
|
|
|
|
|
content = String,
|
|
|
|
|
|
|
|
description = "Content to store as new item",
|
|
|
|
|
|
|
|
content_type = "application/octet-stream"
|
|
|
|
),
|
|
|
|
),
|
|
|
|
security(
|
|
|
|
security(
|
|
|
|
("bearerAuth" = [])
|
|
|
|
("bearerAuth" = [])
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
tag = "item"
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
pub async fn handle_post_item(
|
|
|
|
pub async fn handle_post_item(
|
|
|
|
State(_state): State<AppState>,
|
|
|
|
State(_state): State<AppState>,
|
|
|
|
@@ -150,17 +159,19 @@ pub async fn handle_post_item(
|
|
|
|
delete,
|
|
|
|
delete,
|
|
|
|
path = "/api/item/{item_id}",
|
|
|
|
path = "/api/item/{item_id}",
|
|
|
|
responses(
|
|
|
|
responses(
|
|
|
|
(status = 200, description = "Successfully deleted item"),
|
|
|
|
(status = 200, description = "Successfully deleted item and associated metadata"),
|
|
|
|
(status = 401, description = "Unauthorized"),
|
|
|
|
(status = 400, description = "Bad request - Invalid item ID"),
|
|
|
|
(status = 404, description = "Item not found"),
|
|
|
|
(status = 401, description = "Unauthorized - Invalid or missing authentication credentials"),
|
|
|
|
(status = 500, description = "Internal server error")
|
|
|
|
(status = 404, description = "Item not found - No item exists with the specified ID"),
|
|
|
|
|
|
|
|
(status = 500, description = "Internal server error - Failed to delete item")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
params(
|
|
|
|
params(
|
|
|
|
("item_id" = i64, Path, description = "ID of the item to delete")
|
|
|
|
("item_id" = i64, Path, description = "Unique identifier of the item to delete (must be positive)")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
security(
|
|
|
|
security(
|
|
|
|
("bearerAuth" = [])
|
|
|
|
("bearerAuth" = [])
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
tag = "item"
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
pub async fn handle_delete_item(
|
|
|
|
pub async fn handle_delete_item(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
State(state): State<AppState>,
|
|
|
|
@@ -194,22 +205,23 @@ pub async fn handle_delete_item(
|
|
|
|
get,
|
|
|
|
get,
|
|
|
|
path = "/api/item/latest",
|
|
|
|
path = "/api/item/latest",
|
|
|
|
responses(
|
|
|
|
responses(
|
|
|
|
(status = 200, description = "Successfully retrieved latest item content", body = ApiResponse<String>),
|
|
|
|
(status = 200, description = "Successfully retrieved latest item with content and metadata. Content is included if item is text-based, otherwise only metadata is returned.", body = ApiResponse<ItemContentInfo>),
|
|
|
|
(status = 401, description = "Unauthorized"),
|
|
|
|
(status = 401, description = "Unauthorized - Invalid or missing authentication credentials"),
|
|
|
|
(status = 404, description = "Item not found"),
|
|
|
|
(status = 404, description = "Item not found - No items exist or no items match the specified tags"),
|
|
|
|
(status = 500, description = "Internal server error")
|
|
|
|
(status = 500, description = "Internal server error - Failed to retrieve item content")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
params(
|
|
|
|
params(
|
|
|
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by")
|
|
|
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by (e.g., 'important,work'). If specified, returns the latest item matching ALL tags.")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
security(
|
|
|
|
security(
|
|
|
|
("bearerAuth" = [])
|
|
|
|
("bearerAuth" = [])
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
tag = "item"
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
pub async fn handle_get_item_latest(
|
|
|
|
pub async fn handle_get_item_latest(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
State(state): State<AppState>,
|
|
|
|
Query(params): Query<TagsQuery>,
|
|
|
|
Query(params): Query<TagsQuery>,
|
|
|
|
) -> Result<Json<ApiResponse<String>>, StatusCode> {
|
|
|
|
) -> Result<Json<ApiResponse<ItemContentInfo>>, StatusCode> {
|
|
|
|
|
|
|
|
|
|
|
|
let mut conn = state.db.lock().await;
|
|
|
|
let mut conn = state.db.lock().await;
|
|
|
|
|
|
|
|
|
|
|
|
@@ -228,18 +240,18 @@ pub async fn handle_get_item_latest(
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(item) = item {
|
|
|
|
if let Some(item) = item {
|
|
|
|
match get_item_content(&item, &state.data_dir).await {
|
|
|
|
match get_item_content_info(&item, &state.data_dir, &mut *conn).await {
|
|
|
|
Ok(content) => {
|
|
|
|
Ok(content_info) => {
|
|
|
|
let response = ApiResponse {
|
|
|
|
let response = ApiResponse {
|
|
|
|
success: true,
|
|
|
|
success: true,
|
|
|
|
data: Some(content),
|
|
|
|
data: Some(content_info),
|
|
|
|
error: None,
|
|
|
|
error: None,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
Ok(Json(response))
|
|
|
|
Ok(Json(response))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Failed to get content for item {}: {}", item.id.unwrap_or(0), e);
|
|
|
|
warn!("Failed to get content for item {}: {}", item.id.unwrap_or(0), e);
|
|
|
|
let response = ApiResponse::<String> {
|
|
|
|
let response = ApiResponse::<ItemContentInfo> {
|
|
|
|
success: false,
|
|
|
|
success: false,
|
|
|
|
data: None,
|
|
|
|
data: None,
|
|
|
|
error: Some(format!("Failed to retrieve content: {}", e)),
|
|
|
|
error: Some(format!("Failed to retrieve content: {}", e)),
|
|
|
|
@@ -256,22 +268,24 @@ pub async fn handle_get_item_latest(
|
|
|
|
get,
|
|
|
|
get,
|
|
|
|
path = "/api/item/{item_id}",
|
|
|
|
path = "/api/item/{item_id}",
|
|
|
|
responses(
|
|
|
|
responses(
|
|
|
|
(status = 200, description = "Successfully retrieved item content", body = ApiResponse<String>),
|
|
|
|
(status = 200, description = "Successfully retrieved item with content and metadata. Content is included if item is text-based, otherwise only metadata is returned.", body = ApiResponse<ItemContentInfo>),
|
|
|
|
(status = 401, description = "Unauthorized"),
|
|
|
|
(status = 400, description = "Bad request - Invalid item ID"),
|
|
|
|
(status = 404, description = "Item not found"),
|
|
|
|
(status = 401, description = "Unauthorized - Invalid or missing authentication credentials"),
|
|
|
|
(status = 500, description = "Internal server error")
|
|
|
|
(status = 404, description = "Item not found - No item exists with the specified ID"),
|
|
|
|
|
|
|
|
(status = 500, description = "Internal server error - Failed to retrieve item content")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
params(
|
|
|
|
params(
|
|
|
|
("item_id" = i64, Path, description = "ID of the item to retrieve")
|
|
|
|
("item_id" = i64, Path, description = "Unique identifier of the item to retrieve (must be positive)")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
security(
|
|
|
|
security(
|
|
|
|
("bearerAuth" = [])
|
|
|
|
("bearerAuth" = [])
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
tag = "item"
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
pub async fn handle_get_item(
|
|
|
|
pub async fn handle_get_item(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
State(state): State<AppState>,
|
|
|
|
Path(item_id): Path<i64>,
|
|
|
|
Path(item_id): Path<i64>,
|
|
|
|
) -> Result<Json<ApiResponse<String>>, StatusCode> {
|
|
|
|
) -> Result<Json<ApiResponse<ItemContentInfo>>, StatusCode> {
|
|
|
|
// Validate that item ID is positive to prevent path traversal issues
|
|
|
|
// Validate that item ID is positive to prevent path traversal issues
|
|
|
|
if item_id <= 0 {
|
|
|
|
if item_id <= 0 {
|
|
|
|
return Err(StatusCode::BAD_REQUEST);
|
|
|
|
return Err(StatusCode::BAD_REQUEST);
|
|
|
|
@@ -283,18 +297,18 @@ pub async fn handle_get_item(
|
|
|
|
warn!("Failed to get item {} for content: {}", item_id, e);
|
|
|
|
warn!("Failed to get item {} for content: {}", item_id, e);
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR
|
|
|
|
})? {
|
|
|
|
})? {
|
|
|
|
match get_item_content(&item, &state.data_dir).await {
|
|
|
|
match get_item_content_info(&item, &state.data_dir, &mut *conn).await {
|
|
|
|
Ok(content) => {
|
|
|
|
Ok(content_info) => {
|
|
|
|
let response = ApiResponse {
|
|
|
|
let response = ApiResponse {
|
|
|
|
success: true,
|
|
|
|
success: true,
|
|
|
|
data: Some(content),
|
|
|
|
data: Some(content_info),
|
|
|
|
error: None,
|
|
|
|
error: None,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
Ok(Json(response))
|
|
|
|
Ok(Json(response))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Failed to get content for item {}: {}", item_id, e);
|
|
|
|
warn!("Failed to get content for item {}: {}", item_id, e);
|
|
|
|
let response = ApiResponse::<String> {
|
|
|
|
let response = ApiResponse::<ItemContentInfo> {
|
|
|
|
success: false,
|
|
|
|
success: false,
|
|
|
|
data: None,
|
|
|
|
data: None,
|
|
|
|
error: Some(format!("Failed to retrieve content: {}", e)),
|
|
|
|
error: Some(format!("Failed to retrieve content: {}", e)),
|
|
|
|
@@ -311,17 +325,18 @@ pub async fn handle_get_item(
|
|
|
|
get,
|
|
|
|
get,
|
|
|
|
path = "/api/item/latest/content",
|
|
|
|
path = "/api/item/latest/content",
|
|
|
|
responses(
|
|
|
|
responses(
|
|
|
|
(status = 200, description = "Successfully retrieved latest item raw content"),
|
|
|
|
(status = 200, description = "Successfully retrieved latest item raw content with appropriate MIME type header"),
|
|
|
|
(status = 401, description = "Unauthorized"),
|
|
|
|
(status = 401, description = "Unauthorized - Invalid or missing authentication credentials"),
|
|
|
|
(status = 404, description = "Item not found"),
|
|
|
|
(status = 404, description = "Item not found - No items exist or no items match the specified tags"),
|
|
|
|
(status = 500, description = "Internal server error")
|
|
|
|
(status = 500, description = "Internal server error - Failed to retrieve item content")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
params(
|
|
|
|
params(
|
|
|
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by")
|
|
|
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by (e.g., 'important,work'). If specified, returns the latest item matching ALL tags.")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
security(
|
|
|
|
security(
|
|
|
|
("bearerAuth" = [])
|
|
|
|
("bearerAuth" = [])
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
tag = "item"
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
pub async fn handle_get_item_latest_content(
|
|
|
|
pub async fn handle_get_item_latest_content(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
State(state): State<AppState>,
|
|
|
|
@@ -368,17 +383,19 @@ pub async fn handle_get_item_latest_content(
|
|
|
|
get,
|
|
|
|
get,
|
|
|
|
path = "/api/item/{item_id}/content",
|
|
|
|
path = "/api/item/{item_id}/content",
|
|
|
|
responses(
|
|
|
|
responses(
|
|
|
|
(status = 200, description = "Successfully retrieved item raw content"),
|
|
|
|
(status = 200, description = "Successfully retrieved item raw content with appropriate MIME type header"),
|
|
|
|
(status = 401, description = "Unauthorized"),
|
|
|
|
(status = 400, description = "Bad request - Invalid item ID"),
|
|
|
|
(status = 404, description = "Item not found"),
|
|
|
|
(status = 401, description = "Unauthorized - Invalid or missing authentication credentials"),
|
|
|
|
(status = 500, description = "Internal server error")
|
|
|
|
(status = 404, description = "Item not found - No item exists with the specified ID"),
|
|
|
|
|
|
|
|
(status = 500, description = "Internal server error - Failed to retrieve item content")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
params(
|
|
|
|
params(
|
|
|
|
("item_id" = i64, Path, description = "ID of the item to retrieve")
|
|
|
|
("item_id" = i64, Path, description = "Unique identifier of the item to retrieve (must be positive)")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
security(
|
|
|
|
security(
|
|
|
|
("bearerAuth" = [])
|
|
|
|
("bearerAuth" = [])
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
tag = "item"
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
pub async fn handle_get_item_content(
|
|
|
|
pub async fn handle_get_item_content(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
State(state): State<AppState>,
|
|
|
|
@@ -436,6 +453,57 @@ async fn get_item_content(item: &db::Item, data_dir: &PathBuf) -> Result<String>
|
|
|
|
Ok(content)
|
|
|
|
Ok(content)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async fn get_item_content_info(item: &db::Item, data_dir: &PathBuf, conn: &mut rusqlite::Connection) -> Result<ItemContentInfo> {
|
|
|
|
|
|
|
|
let item_id = item.id.ok_or_else(|| anyhow!("Item missing ID"))?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Validate that item ID is positive to prevent path traversal issues
|
|
|
|
|
|
|
|
if item_id <= 0 {
|
|
|
|
|
|
|
|
return Err(anyhow!("Invalid item ID: {}", item_id));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get metadata
|
|
|
|
|
|
|
|
let meta_entries = db::get_item_meta(conn, item)
|
|
|
|
|
|
|
|
.map_err(|e| anyhow!("Failed to get metadata: {}", e))?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let metadata: HashMap<String, String> = meta_entries
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.map(|m| (m.name.clone(), m.value.clone()))
|
|
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Determine if content is binary
|
|
|
|
|
|
|
|
let is_binary = if let Some(binary_meta) = metadata.get("binary") {
|
|
|
|
|
|
|
|
binary_meta == "true"
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Fall back to checking the actual content
|
|
|
|
|
|
|
|
let mut item_path = data_dir.clone();
|
|
|
|
|
|
|
|
item_path.push(item_id.to_string());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let compression_type = CompressionType::from_str(&item.compression)?;
|
|
|
|
|
|
|
|
let compression_engine = get_compression_engine(compression_type)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut reader = compression_engine.open(item_path)?;
|
|
|
|
|
|
|
|
let mut buffer = [0u8; 8192]; // Read first 8KB to check
|
|
|
|
|
|
|
|
let bytes_read = reader.read(&mut buffer)?;
|
|
|
|
|
|
|
|
is_binary(&buffer[..bytes_read])
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get content if not binary
|
|
|
|
|
|
|
|
let content = if is_binary {
|
|
|
|
|
|
|
|
None
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
match get_item_content(item, data_dir).await {
|
|
|
|
|
|
|
|
Ok(content_str) => Some(content_str),
|
|
|
|
|
|
|
|
Err(_) => None, // If we can't read as string, treat as binary
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(ItemContentInfo {
|
|
|
|
|
|
|
|
metadata,
|
|
|
|
|
|
|
|
content,
|
|
|
|
|
|
|
|
binary: is_binary,
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fn get_item_raw_content(item: &db::Item, data_dir: &PathBuf, conn: &mut rusqlite::Connection) -> Result<(Vec<u8>, String)> {
|
|
|
|
async fn get_item_raw_content(item: &db::Item, data_dir: &PathBuf, conn: &mut rusqlite::Connection) -> Result<(Vec<u8>, String)> {
|
|
|
|
let item_id = item.id.ok_or_else(|| anyhow!("Item missing ID"))?;
|
|
|
|
let item_id = item.id.ok_or_else(|| anyhow!("Item missing ID"))?;
|
|
|
|
|
|
|
|
|
|
|
|
@@ -472,17 +540,18 @@ async fn get_item_raw_content(item: &db::Item, data_dir: &PathBuf, conn: &mut ru
|
|
|
|
get,
|
|
|
|
get,
|
|
|
|
path = "/api/item/latest/meta",
|
|
|
|
path = "/api/item/latest/meta",
|
|
|
|
responses(
|
|
|
|
responses(
|
|
|
|
(status = 200, description = "Successfully retrieved latest item metadata", body = ApiResponse<HashMap<String, String>>),
|
|
|
|
(status = 200, description = "Successfully retrieved latest item metadata including file type, encoding, size, and other system information", body = ApiResponse<HashMap<String, String>>),
|
|
|
|
(status = 401, description = "Unauthorized"),
|
|
|
|
(status = 401, description = "Unauthorized - Invalid or missing authentication credentials"),
|
|
|
|
(status = 404, description = "Item not found"),
|
|
|
|
(status = 404, description = "Item not found - No items exist or no items match the specified tags"),
|
|
|
|
(status = 500, description = "Internal server error")
|
|
|
|
(status = 500, description = "Internal server error - Failed to retrieve item metadata")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
params(
|
|
|
|
params(
|
|
|
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by")
|
|
|
|
("tags" = Option<String>, Query, description = "Comma-separated list of tags to filter by (e.g., 'important,work'). If specified, returns the latest item matching ALL tags.")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
security(
|
|
|
|
security(
|
|
|
|
("bearerAuth" = [])
|
|
|
|
("bearerAuth" = [])
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
tag = "item"
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
pub async fn handle_get_item_latest_meta(
|
|
|
|
pub async fn handle_get_item_latest_meta(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
State(state): State<AppState>,
|
|
|
|
@@ -531,17 +600,19 @@ pub async fn handle_get_item_latest_meta(
|
|
|
|
get,
|
|
|
|
get,
|
|
|
|
path = "/api/item/{item_id}/meta",
|
|
|
|
path = "/api/item/{item_id}/meta",
|
|
|
|
responses(
|
|
|
|
responses(
|
|
|
|
(status = 200, description = "Successfully retrieved item metadata", body = ApiResponse<HashMap<String, String>>),
|
|
|
|
(status = 200, description = "Successfully retrieved item metadata including file type, encoding, size, and other system information", body = ApiResponse<HashMap<String, String>>),
|
|
|
|
(status = 401, description = "Unauthorized"),
|
|
|
|
(status = 400, description = "Bad request - Invalid item ID"),
|
|
|
|
(status = 404, description = "Item not found"),
|
|
|
|
(status = 401, description = "Unauthorized - Invalid or missing authentication credentials"),
|
|
|
|
(status = 500, description = "Internal server error")
|
|
|
|
(status = 404, description = "Item not found - No item exists with the specified ID"),
|
|
|
|
|
|
|
|
(status = 500, description = "Internal server error - Failed to retrieve item metadata")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
params(
|
|
|
|
params(
|
|
|
|
("item_id" = i64, Path, description = "ID of the item to retrieve metadata for")
|
|
|
|
("item_id" = i64, Path, description = "Unique identifier of the item to retrieve metadata for (must be positive)")
|
|
|
|
),
|
|
|
|
),
|
|
|
|
security(
|
|
|
|
security(
|
|
|
|
("bearerAuth" = [])
|
|
|
|
("bearerAuth" = [])
|
|
|
|
)
|
|
|
|
),
|
|
|
|
|
|
|
|
tag = "item"
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
pub async fn handle_get_item_meta(
|
|
|
|
pub async fn handle_get_item_meta(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
State(state): State<AppState>,
|
|
|
|
|