feat(drive): remove all owner_id from {File,Folder}Dto

This commit is contained in:
Edouard Vanbelle
2026-07-03 00:48:14 +02:00
parent 29bcf48eb7
commit 37467ed9d3
33 changed files with 116 additions and 149 deletions
@@ -61,7 +61,10 @@ impl PluginLifecycleHook {
dispatch.dispatch(PluginEvent {
name: EVENT_FILE_UPLOADED,
user_id: dto.owner_id,
// Post-D7 the wire DTO no longer carries `owner_id`;
// §14 `created_by` provenance is the equivalent signal
// (who put the file in the system).
user_id: dto.created_by.map(|u| u.to_string()),
invocation_id: Uuid::new_v4().to_string(),
payload: serde_json::json!({
"path": dto.path,
-9
View File
@@ -55,11 +55,6 @@ pub struct FavoriteItemDto {
#[serde(skip_serializing_if = "Option::is_none")]
pub item_path: Option<String>,
/// UUID of the file/folder's actual owner (may differ from `user_id` when
/// the item was shared and then favourited by another user).
#[serde(skip_serializing_if = "Option::is_none")]
pub owner_id: Option<String>,
// ── Pre-computed display fields ──
/// FontAwesome icon CSS class (e.g. "fas fa-file-image", "fas fa-folder")
pub icon_class: String,
@@ -124,10 +119,6 @@ pub struct FavoriteResourceRow {
pub size: i64,
pub resource_created_at: DateTime<Utc>,
pub modified_at: DateTime<Utc>,
/// Post-D7: nullable on new rows (the legacy `storage.{files,folders}.user_id`
/// column is no longer written). `is_owner` is now `false` when
/// this is `None` — see the SQL projection in favorites repo.
pub owner_id: Option<Uuid>,
/// Drive that owns this row. Surfaced on the favorites listing
/// so a UI can tell when a favorited item lives in a different
/// drive than the user's home (post-D6 cross-drive moves +
+3 -10
View File
@@ -54,10 +54,6 @@ pub struct FileDto {
/// Human-readable formatted size (e.g. "3.27 MB")
pub size_formatted: String,
/// Owner user ID (omitted from JSON when None)
#[serde(skip_serializing_if = "Option::is_none")]
pub owner_id: Option<String>,
/// Sort date for Photos timeline — COALESCE(EXIF captured_at, created_at).
/// Only populated by the /api/photos endpoint.
#[serde(skip_serializing_if = "Option::is_none")]
@@ -102,7 +98,7 @@ impl From<File> for FileDto {
let content_hash = file.content_hash().to_string();
// Consume the entity by moving all fields — zero heap allocations
// for id, name, path, folder_id, owner_id (previously 5× .to_string()).
// for id, name, path, folder_id (previously 4× .to_string()).
let parts = file.into_parts();
let icon_class = Arc::from(icon_class_for(&parts.name, &parts.mime_type));
@@ -124,7 +120,6 @@ impl From<File> for FileDto {
icon_special_class,
category,
size_formatted,
owner_id: parts.owner_id.map(|u| u.to_string()),
sort_date: None,
content_hash,
etag,
@@ -157,9 +152,8 @@ impl FileDto {
///
/// Used when a file is returned to a share recipient: `path` reveals the
/// full folder hierarchy above the file which the recipient may not have
/// access to. `folder_id` and `owner_id` are intentionally kept — the
/// former is needed for sub-folder navigation (covered by the cascade
/// grant), and the latter is harmless metadata.
/// access to. `folder_id` is intentionally kept — it's needed for
/// sub-folder navigation (covered by the cascade grant).
#[must_use]
pub fn without_hierarchy_info(self) -> Self {
Self {
@@ -183,7 +177,6 @@ impl FileDto {
icon_special_class: Arc::from(""),
category: Arc::from("Document"),
size_formatted: "0 Bytes".to_string(),
owner_id: None,
content_hash: String::new(),
etag: String::new(),
sort_date: None,
+2 -14
View File
@@ -48,10 +48,6 @@ pub struct FolderDto {
/// Parent folder ID
pub parent_id: Option<String>,
/// Owner user ID (scopes visibility per user)
#[serde(skip_serializing_if = "Option::is_none")]
pub owner_id: Option<String>,
/// Drive that owns this folder. The scope axis for path-based
/// lookups across REST / WebDAV / NextCloud / CalDAV / CardDAV.
/// Post-D0 `storage.folders.drive_id` is `NOT NULL`; stub /
@@ -111,7 +107,6 @@ impl From<Folder> for FolderDto {
name: folder.name().to_string(),
path: folder.path_string().to_string(),
parent_id: folder.parent_id().map(String::from),
owner_id: folder.owner_id().map(|u| u.to_string()),
drive_id: folder.drive_id(),
created_at: folder.created_at(),
modified_at: folder.modified_at(),
@@ -147,9 +142,8 @@ impl FolderDto {
///
/// Used when a folder is returned to a share recipient: `path` reveals the
/// full folder hierarchy above the shared folder which the recipient may
/// not have access to. `parent_id` and `owner_id` are intentionally kept
/// — the former is needed for sub-folder navigation (covered by the
/// cascade grant), and the latter is harmless metadata.
/// not have access to. `parent_id` is intentionally kept — it's needed
/// for sub-folder navigation (covered by the cascade grant).
#[must_use]
pub fn without_hierarchy_info(self) -> Self {
Self {
@@ -165,7 +159,6 @@ impl FolderDto {
name: "stub-folder".to_string(),
path: "/stub/path".to_string(),
parent_id: None,
owner_id: None,
drive_id: Uuid::nil(),
created_at: 0,
modified_at: 0,
@@ -205,11 +198,6 @@ pub struct FolderResourceRow {
pub size: i64,
pub created_at: DateTime<Utc>,
pub modified_at: DateTime<Utc>,
/// Post-D7: the legacy `user_id` column on `storage.{files,folders}`
/// is nullable — new rows leave it NULL — so this optional. UI
/// surfaces should prefer `created_by` / `updated_by` on the
/// per-resource DTO instead.
pub owner_id: Option<Uuid>,
/// Drive that owns this row. Same column as
/// `storage.folders.drive_id` / `storage.files.drive_id`. Surfaced
/// on the listing so a UI can tell when a child lives in a
-4
View File
@@ -104,10 +104,6 @@ pub struct RecentResourceRow {
pub size: i64,
pub resource_created_at: DateTime<Utc>,
pub modified_at: DateTime<Utc>,
/// Post-D7: nullable on new rows (the legacy
/// `storage.{files,folders}.user_id` column is no longer written).
/// Consumers should prefer §14 provenance columns.
pub owner_id: Option<Uuid>,
/// Drive that owns this row. Surfaced on the recent listing
/// so a UI can tell when a recently-accessed item lives in a
/// different drive than the user's home (post-D6 cross-drive
-4
View File
@@ -60,10 +60,6 @@ pub struct TrashResourceRow {
pub size: i64,
pub resource_created_at: DateTime<Utc>,
pub modified_at: DateTime<Utc>,
/// Post-D7: nullable on new rows (the legacy `storage.{files,folders}.user_id`
/// column is no longer written). Consumers should prefer §14
/// provenance columns when available.
pub owner_id: Option<Uuid>,
/// Drive the trashed item belongs to. Surfaced verbatim on the wire
/// (`TrashResourceItemDto.drive_id`) so the `/trash` UI can group by
/// drive without an extra lookup per row. D2b: filtering by drive is
+8 -10
View File
@@ -241,23 +241,21 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
.collect())
}
/// Like [`list_files_batch`], but scoped to a specific owner.
/// Like [`list_files_batch`], but scoped to a specific caller.
///
/// Used by streaming WebDAV PROPFIND so that each user only sees their
/// own files, even in shared folder_id namespaces.
/// Used by streaming WebDAV PROPFIND. Post-D7 the concrete
/// implementation in `FileRetrievalService` uses drive-membership
/// grants; this default falls back to the unscoped listing (the
/// caller passes through `owner_id` for interface parity but the
/// stub can't apply a real filter without a repo lookup).
async fn list_files_batch_with_perms(
&self,
folder_id: Option<&str>,
owner_id: Uuid,
_owner_id: Uuid,
offset: i64,
limit: i64,
) -> Result<Vec<FileDto>, DomainError> {
let all = self.list_files_batch(folder_id, offset, limit).await?;
let owner_str = owner_id.to_string();
Ok(all
.into_iter()
.filter(|f| f.owner_id.as_deref().is_some_and(|o| o == owner_str))
.collect())
self.list_files_batch(folder_id, offset, limit).await
}
}
@@ -962,7 +962,6 @@ fn row_to_item_dto(row: TrashResourceRow) -> TrashResourceItemDto {
name: row.name.clone(),
path,
parent_id: row.parent_id.map(|u| u.to_string()),
owner_id: row.owner_id.map(|u| u.to_string()),
// D2b: the trash listing query now SELECTs `drive_id` (the
// unified view exposes it). Surfaced so per-drive grouping
// in the `/trash` UI doesn't need an extra lookup per row.
@@ -1013,7 +1012,6 @@ fn row_to_item_dto(row: TrashResourceRow) -> TrashResourceItemDto {
icon_special_class: std::sync::Arc::from(icon_special_class_for(&row.name, mime)),
category: std::sync::Arc::from(category_for(&row.name, mime)),
size_formatted: format_file_size(size_bytes),
owner_id: row.owner_id.map(|u| u.to_string()),
sort_date: None,
content_hash,
etag,