feat(drive): remove all owner_id from {File,Folder}Dto
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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 +
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user