feat(server): add file_size to API ItemInfo response
This commit is contained in:
@@ -178,6 +178,7 @@ pub async fn handle_list_items(
|
||||
let item_infos: Vec<ItemInfo> = items_with_meta
|
||||
.into_iter()
|
||||
.filter_map(|iwm| ItemInfo::try_from(iwm).ok())
|
||||
.map(|info| info.with_file_size(&state.data_dir))
|
||||
.collect();
|
||||
|
||||
ResponseBuilder::json(ApiResponse::ok(item_infos))
|
||||
@@ -339,6 +340,7 @@ pub async fn handle_post_item(
|
||||
let db = state.db.clone();
|
||||
let item_service = state.item_service.clone();
|
||||
let settings = state.settings.clone();
|
||||
let data_dir = state.data_dir.clone();
|
||||
|
||||
// Parse tags from query parameter
|
||||
let tags: Vec<String> = params
|
||||
@@ -472,10 +474,12 @@ pub async fn handle_post_item(
|
||||
return Err(StatusCode::PAYLOAD_TOO_LARGE);
|
||||
}
|
||||
|
||||
let item_info = ItemInfo::try_from(item_with_meta).map_err(|e| {
|
||||
warn!("Item conversion failed: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
let item_info = ItemInfo::try_from(item_with_meta)
|
||||
.map(|info| info.with_file_size(&data_dir))
|
||||
.map_err(|e| {
|
||||
warn!("Item conversion failed: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(Json(ApiResponse::ok(item_info)))
|
||||
}
|
||||
@@ -1092,6 +1096,7 @@ pub async fn handle_delete_item(
|
||||
compression: deleted_item.compression,
|
||||
tags: vec![],
|
||||
metadata: HashMap::new(),
|
||||
file_size: None,
|
||||
};
|
||||
|
||||
Ok(Json(ApiResponse::ok(item_info)))
|
||||
@@ -1124,6 +1129,7 @@ pub async fn handle_get_item_info(
|
||||
|
||||
let db = state.db.clone();
|
||||
let item_service = state.item_service.clone();
|
||||
let data_dir = state.data_dir.clone();
|
||||
|
||||
let item_with_meta = task::spawn_blocking(move || {
|
||||
let conn = db.blocking_lock();
|
||||
@@ -1136,10 +1142,12 @@ pub async fn handle_get_item_info(
|
||||
})?
|
||||
.map_err(handle_item_error)?;
|
||||
|
||||
let item_info = ItemInfo::try_from(item_with_meta).map_err(|e| {
|
||||
warn!("Item conversion failed: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
let item_info = ItemInfo::try_from(item_with_meta)
|
||||
.map(|info| info.with_file_size(&data_dir))
|
||||
.map_err(|e| {
|
||||
warn!("Item conversion failed: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
Ok(Json(ApiResponse::ok(item_info)))
|
||||
}
|
||||
@@ -1352,6 +1360,7 @@ pub async fn handle_update_item(
|
||||
let db = state.db.clone();
|
||||
let item_service = state.item_service.clone();
|
||||
let settings = state.settings.clone();
|
||||
let data_dir = state.data_dir.clone();
|
||||
let size_param = params.uncompressed_size;
|
||||
|
||||
let item_info = task::spawn_blocking(move || {
|
||||
@@ -1369,10 +1378,12 @@ pub async fn handle_update_item(
|
||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
match item_service.get_item(&conn, item_id) {
|
||||
Ok(iwm) => ItemInfo::try_from(iwm).map_err(|e| {
|
||||
warn!("Item conversion failed: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}),
|
||||
Ok(iwm) => ItemInfo::try_from(iwm)
|
||||
.map(|info| info.with_file_size(&data_dir))
|
||||
.map_err(|e| {
|
||||
warn!("Item conversion failed: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}),
|
||||
Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR),
|
||||
}
|
||||
}
|
||||
@@ -1392,10 +1403,12 @@ pub async fn handle_update_item(
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(item_with_meta) => ItemInfo::try_from(item_with_meta).map_err(|e| {
|
||||
warn!("Item conversion failed: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}),
|
||||
Ok(item_with_meta) => ItemInfo::try_from(item_with_meta)
|
||||
.map(|info| info.with_file_size(&data_dir))
|
||||
.map_err(|e| {
|
||||
warn!("Item conversion failed: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}),
|
||||
Err(CoreError::ItemNotFound(_)) => Err(StatusCode::NOT_FOUND),
|
||||
Err(e) => {
|
||||
warn!("Failed to update item {item_id}: {e}");
|
||||
|
||||
@@ -366,10 +366,13 @@ pub struct StatusInfoResponse {
|
||||
/// let item_info = ItemInfo {
|
||||
/// id: 42,
|
||||
/// ts: "2023-12-01T15:30:45Z".to_string(),
|
||||
/// size: Some(1024),
|
||||
/// uncompressed_size: Some(1024),
|
||||
/// compressed_size: Some(512),
|
||||
/// closed: true,
|
||||
/// compression: "gzip".to_string(),
|
||||
/// tags: vec!["important".to_string()],
|
||||
/// metadata: HashMap::from([("mime_type".to_string(), "text/plain".to_string())]),
|
||||
/// file_size: Some(512),
|
||||
/// };
|
||||
/// ```
|
||||
#[derive(Serialize, Deserialize, ToSchema)]
|
||||
@@ -413,6 +416,33 @@ pub struct ItemInfo {
|
||||
/// Key-value pairs containing additional metadata about the item.
|
||||
#[schema(example = json!({"mime_type": "text/plain", "mime_encoding": "utf-8", "line_count": "42"}))]
|
||||
pub metadata: HashMap<String, String>,
|
||||
/// Actual file size in bytes.
|
||||
///
|
||||
/// The filesystem-reported size of the item's data file. This may differ from
|
||||
/// `compressed_size` if the file was written and the database hasn't been updated.
|
||||
/// None if the file cannot be read (e.g., file not found, permission denied).
|
||||
#[schema(example = 512)]
|
||||
pub file_size: Option<i64>,
|
||||
}
|
||||
|
||||
impl ItemInfo {
|
||||
/// Enriches this `ItemInfo` with the actual filesystem-reported size.
|
||||
///
|
||||
/// Reads the size of the item's data file from disk and sets `file_size`.
|
||||
/// If the file cannot be read, `file_size` is left as None.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `data_dir` - The data directory path containing item files.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new `ItemInfo` with `file_size` populated from the filesystem.
|
||||
pub fn with_file_size(mut self, data_dir: &std::path::Path) -> Self {
|
||||
let item_path = data_dir.join(self.id.to_string());
|
||||
self.file_size = std::fs::metadata(&item_path).map(|m| m.len() as i64).ok();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ItemWithMeta> for ItemInfo {
|
||||
@@ -433,6 +463,7 @@ impl TryFrom<ItemWithMeta> for ItemInfo {
|
||||
compression: item_with_meta.item.compression,
|
||||
tags,
|
||||
metadata,
|
||||
file_size: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user