feat(content_hash): propagate etag and hash_content to */resources

This commit is contained in:
Edouard Vanbelle
2026-06-06 19:51:51 +02:00
parent 46f8789f4f
commit bde7c83932
17 changed files with 166 additions and 54 deletions
+17 -10
View File
@@ -26,6 +26,7 @@ use crate::application::ports::folder_ports::FolderUseCase;
use crate::application::ports::trash_ports::TrashUseCase;
use crate::application::services::folder_service::FolderService;
use crate::common::di::AppState as GlobalAppState;
use crate::domain::entities::file::File;
use crate::interfaces::errors::AppError;
use crate::interfaces::middleware::auth::AuthUser;
@@ -717,14 +718,20 @@ pub async fn list_folder_resources(
.as_deref()
.unwrap_or("application/octet-stream");
let size_bytes = row.size.max(0) as u64;
// `FolderResourceRow` (the UNION ALL row used
// by this listing) doesn't carry `blob_hash`,
// so neither `content_hash` nor `etag` can be
// populated here without widening the SQL. The
// REST file-listing UI doesn't issue
// conditional requests against these rows —
// file download / WebDAV PROPFIND go through
// paths that DO carry the hash.
// `blob_hash` is `Some(_)` for file rows in the
// UNION ALL (`NULL` for folders). Route the
// ETag formula through `File::compute_etag` —
// the single source of truth shared with
// GET/HEAD/PROPFIND/PUT response — so this
// listing's `etag` byte-equals what a
// conditional request would compare against.
let modified_at_u = row.modified_at.timestamp() as u64;
let content_hash = row.blob_hash.clone().unwrap_or_default();
let etag = if content_hash.is_empty() {
String::new()
} else {
File::compute_etag(&content_hash, modified_at_u)
};
let dto = FileDto {
id: row.id.to_string(),
name: row.name.clone(),
@@ -740,8 +747,8 @@ pub async fn list_folder_resources(
size_formatted: format_file_size(size_bytes),
owner_id: Some(row.owner_id.to_string()),
sort_date: None,
content_hash: String::new(),
etag: String::new(),
content_hash,
etag,
};
FolderResourceItemDto {
resource_type: ResourceTypeDto::File,