feat(drive): remove all owner_id from {File,Folder}Dto
This commit is contained in:
@@ -41,8 +41,7 @@ impl FavoritesRepositoryPort for FavoritesPgRepository {
|
||||
WHEN uf.item_type = 'folder' THEN fld.path
|
||||
WHEN uf.item_type = 'file' THEN COALESCE(pfld.path || '/' || f.name, f.name)
|
||||
ELSE NULL
|
||||
END AS "item_path",
|
||||
COALESCE(f.user_id, fld.user_id)::TEXT AS "owner_id"
|
||||
END AS "item_path"
|
||||
FROM auth.user_favorites uf
|
||||
LEFT JOIN storage.files f ON uf.item_type = 'file'
|
||||
AND f.id = uf.item_id::UUID
|
||||
@@ -82,7 +81,6 @@ impl FavoritesRepositoryPort for FavoritesPgRepository {
|
||||
parent_id: row.try_get("parent_id").ok(),
|
||||
modified_at: row.try_get("modified_at").ok(),
|
||||
item_path: row.try_get("item_path").ok(),
|
||||
owner_id: row.try_get("owner_id").ok(),
|
||||
// Temporary defaults; with_display_fields() computes the real values
|
||||
icon_class: String::new(),
|
||||
icon_special_class: String::new(),
|
||||
@@ -309,10 +307,18 @@ impl FavoritesRepositoryPort for FavoritesPgRepository {
|
||||
-1::bigint AS size,
|
||||
fld.created_at AS resource_created_at,
|
||||
fld.updated_at AS modified_at,
|
||||
fld.user_id AS owner_id,
|
||||
fld.drive_id AS drive_id,
|
||||
NULL::text AS blob_hash,
|
||||
(fld.user_id = $1::uuid) AS is_owner,
|
||||
fld.created_by AS created_by,
|
||||
EXISTS (
|
||||
SELECT 1 FROM storage.role_grants g
|
||||
WHERE g.resource_type = 'drive'
|
||||
AND g.resource_id = fld.drive_id
|
||||
AND g.role = 'owner'
|
||||
AND g.subject_type = 'user'
|
||||
AND g.subject_id = $1::uuid
|
||||
AND (g.expires_at IS NULL OR g.expires_at > NOW())
|
||||
) AS is_owner,
|
||||
uf.created_at AS favorited_at,
|
||||
fld.path::text AS resource_path,
|
||||
LOWER(fld.name) AS sort_str,
|
||||
@@ -333,10 +339,18 @@ impl FavoritesRepositoryPort for FavoritesPgRepository {
|
||||
f.size::bigint,
|
||||
f.created_at AS resource_created_at,
|
||||
f.updated_at AS modified_at,
|
||||
f.user_id AS owner_id,
|
||||
f.drive_id AS drive_id,
|
||||
f.blob_hash,
|
||||
(f.user_id = $1::uuid) AS is_owner,
|
||||
f.created_by AS created_by,
|
||||
EXISTS (
|
||||
SELECT 1 FROM storage.role_grants g
|
||||
WHERE g.resource_type = 'drive'
|
||||
AND g.resource_id = f.drive_id
|
||||
AND g.role = 'owner'
|
||||
AND g.subject_type = 'user'
|
||||
AND g.subject_id = $1::uuid
|
||||
AND (g.expires_at IS NULL OR g.expires_at > NOW())
|
||||
) AS is_owner,
|
||||
uf.created_at AS favorited_at,
|
||||
COALESCE(pfld.path::text || '/' || f.name, f.name) AS resource_path,
|
||||
LOWER(f.name) AS sort_str,
|
||||
@@ -489,7 +503,8 @@ impl FavoritesRepositoryPort for FavoritesPgRepository {
|
||||
};
|
||||
|
||||
let user_join = if need_user_join {
|
||||
"LEFT JOIN auth.users u ON u.id = r.owner_id"
|
||||
// Post-D7: `owner_id` retired; join by `created_by`.
|
||||
"LEFT JOIN auth.users u ON u.id = r.created_by"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
@@ -506,7 +521,7 @@ impl FavoritesRepositoryPort for FavoritesPgRepository {
|
||||
SELECT
|
||||
r.resource_type, r.resource_id, r.name, r.parent_id,
|
||||
r.mime_type, r.size, r.resource_created_at, r.modified_at,
|
||||
r.owner_id, r.drive_id, r.is_owner, r.favorited_at, r.resource_path,
|
||||
r.drive_id, r.is_owner, r.favorited_at, r.resource_path,
|
||||
r.sort_str, r.type_order, r.folder_first{username_col}
|
||||
FROM resources r
|
||||
{user_join}
|
||||
@@ -577,7 +592,6 @@ LIMIT $6"
|
||||
size,
|
||||
resource_created_at: row.get("resource_created_at"),
|
||||
modified_at: row.get("modified_at"),
|
||||
owner_id: row.try_get("owner_id").ok(),
|
||||
drive_id: row.get("drive_id"),
|
||||
blob_hash: row.try_get("blob_hash").ok(),
|
||||
is_owner: row.try_get("is_owner").unwrap_or(false),
|
||||
|
||||
@@ -1390,7 +1390,6 @@ impl FolderDbRepository {
|
||||
-1::bigint AS size,
|
||||
f.created_at,
|
||||
f.updated_at AS modified_at,
|
||||
f.user_id,
|
||||
f.drive_id,
|
||||
NULL::text AS blob_hash,
|
||||
LOWER(f.name) AS sort_str,
|
||||
@@ -1410,7 +1409,6 @@ impl FolderDbRepository {
|
||||
fm.size::bigint,
|
||||
fm.created_at,
|
||||
fm.updated_at AS modified_at,
|
||||
fm.user_id,
|
||||
fm.drive_id,
|
||||
fm.blob_hash,
|
||||
LOWER(fm.name) AS sort_str,
|
||||
@@ -1536,7 +1534,7 @@ impl FolderDbRepository {
|
||||
let sql = format!(
|
||||
"WITH resources AS ({cte_inner}) \
|
||||
SELECT resource_type, id, name, folder_id, mime_type, size, \
|
||||
created_at, modified_at, user_id, drive_id, blob_hash, \
|
||||
created_at, modified_at, drive_id, blob_hash, \
|
||||
sort_str, type_order, folder_first \
|
||||
FROM resources \
|
||||
{where_clause} \
|
||||
@@ -1545,7 +1543,7 @@ impl FolderDbRepository {
|
||||
);
|
||||
|
||||
// Row: (resource_type, id, name, folder_id, mime_type, size,
|
||||
// created_at, modified_at, user_id, drive_id, blob_hash,
|
||||
// created_at, modified_at, drive_id, blob_hash,
|
||||
// sort_str, type_order, folder_first)
|
||||
type Row = (
|
||||
String,
|
||||
@@ -1556,8 +1554,7 @@ impl FolderDbRepository {
|
||||
i64,
|
||||
chrono::DateTime<chrono::Utc>,
|
||||
chrono::DateTime<chrono::Utc>,
|
||||
Option<Uuid>, // user_id (post-D7: nullable on new rows)
|
||||
Uuid,
|
||||
Uuid, // drive_id
|
||||
Option<String>,
|
||||
String,
|
||||
i64,
|
||||
@@ -1588,12 +1585,11 @@ impl FolderDbRepository {
|
||||
size: r.5,
|
||||
created_at: r.6,
|
||||
modified_at: r.7,
|
||||
owner_id: r.8,
|
||||
drive_id: r.9,
|
||||
blob_hash: r.10,
|
||||
sort_str: r.11,
|
||||
type_order: r.12,
|
||||
folder_first: r.13,
|
||||
drive_id: r.8,
|
||||
blob_hash: r.9,
|
||||
sort_str: r.10,
|
||||
type_order: r.11,
|
||||
folder_first: r.12,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -206,6 +206,20 @@ impl RecentItemsRepositoryPort for RecentItemsPgRepository {
|
||||
// ── Build the UNION ALL CTE ─────────────────────────────────────────
|
||||
let mut cte_branches: Vec<&str> = Vec::new();
|
||||
|
||||
// Post-D7: `is_owner` means "the caller holds an Owner
|
||||
// role_grant on the drive owning this row". Personal drives:
|
||||
// the single-owner invariant makes this trivially true for the
|
||||
// owner and false for anyone else. Shared drives: multiple
|
||||
// Owners possible; each of them gets `true`. Used only to gate
|
||||
// whether the handler exposes the full path (path-hierarchy
|
||||
// hiding for share recipients — see `recent_handler.rs`).
|
||||
//
|
||||
// The `created_by` projection is separate — §14 provenance,
|
||||
// used for the "Owner" column and the owner sort's username
|
||||
// JOIN. The two signals genuinely differ post-D2: e.g. Bob
|
||||
// (Editor on Alice's shared drive) making a file has
|
||||
// `created_by = Bob` but `is_owner = false` because Alice owns
|
||||
// the drive.
|
||||
let folder_branch = r#"
|
||||
SELECT
|
||||
'folder'::text AS resource_type,
|
||||
@@ -216,10 +230,18 @@ impl RecentItemsRepositoryPort for RecentItemsPgRepository {
|
||||
-1::bigint AS size,
|
||||
fld.created_at AS resource_created_at,
|
||||
fld.updated_at AS modified_at,
|
||||
fld.user_id AS owner_id,
|
||||
fld.drive_id AS drive_id,
|
||||
NULL::text AS blob_hash,
|
||||
(fld.user_id = $1::uuid) AS is_owner,
|
||||
fld.created_by AS created_by,
|
||||
EXISTS (
|
||||
SELECT 1 FROM storage.role_grants g
|
||||
WHERE g.resource_type = 'drive'
|
||||
AND g.resource_id = fld.drive_id
|
||||
AND g.role = 'owner'
|
||||
AND g.subject_type = 'user'
|
||||
AND g.subject_id = $1::uuid
|
||||
AND (g.expires_at IS NULL OR g.expires_at > NOW())
|
||||
) AS is_owner,
|
||||
ur.accessed_at AS accessed_at,
|
||||
fld.path::text AS resource_path,
|
||||
LOWER(fld.name) AS sort_str,
|
||||
@@ -240,10 +262,18 @@ impl RecentItemsRepositoryPort for RecentItemsPgRepository {
|
||||
f.size::bigint,
|
||||
f.created_at AS resource_created_at,
|
||||
f.updated_at AS modified_at,
|
||||
f.user_id AS owner_id,
|
||||
f.drive_id AS drive_id,
|
||||
f.blob_hash,
|
||||
(f.user_id = $1::uuid) AS is_owner,
|
||||
f.created_by AS created_by,
|
||||
EXISTS (
|
||||
SELECT 1 FROM storage.role_grants g
|
||||
WHERE g.resource_type = 'drive'
|
||||
AND g.resource_id = f.drive_id
|
||||
AND g.role = 'owner'
|
||||
AND g.subject_type = 'user'
|
||||
AND g.subject_id = $1::uuid
|
||||
AND (g.expires_at IS NULL OR g.expires_at > NOW())
|
||||
) AS is_owner,
|
||||
ur.accessed_at AS accessed_at,
|
||||
COALESCE(pfld.path::text || '/' || f.name, f.name) AS resource_path,
|
||||
LOWER(f.name) AS sort_str,
|
||||
@@ -394,7 +424,9 @@ impl RecentItemsRepositoryPort for RecentItemsPgRepository {
|
||||
};
|
||||
|
||||
let user_join = if need_user_join {
|
||||
"LEFT JOIN auth.users u ON u.id = r.owner_id"
|
||||
// Post-D7: `owner_id` column retired; use `created_by`
|
||||
// (§14 provenance) as the "owner" identity for the sort.
|
||||
"LEFT JOIN auth.users u ON u.id = r.created_by"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
@@ -411,7 +443,7 @@ impl RecentItemsRepositoryPort for RecentItemsPgRepository {
|
||||
SELECT
|
||||
r.resource_type, r.resource_id, r.name, r.parent_id,
|
||||
r.mime_type, r.size, r.resource_created_at, r.modified_at,
|
||||
r.owner_id, r.drive_id, r.is_owner, r.accessed_at, r.resource_path,
|
||||
r.drive_id, r.is_owner, r.accessed_at, r.resource_path,
|
||||
r.sort_str, r.type_order, r.folder_first{username_col}
|
||||
FROM resources r
|
||||
{user_join}
|
||||
@@ -486,7 +518,6 @@ LIMIT $6"
|
||||
size,
|
||||
resource_created_at: row.get("resource_created_at"),
|
||||
modified_at: row.get("modified_at"),
|
||||
owner_id: row.try_get("owner_id").ok(),
|
||||
drive_id: row.get("drive_id"),
|
||||
blob_hash: row.try_get("blob_hash").ok(),
|
||||
is_owner: row.try_get("is_owner").unwrap_or(false),
|
||||
|
||||
@@ -374,7 +374,6 @@ impl TrashDbRepository {
|
||||
-1::bigint AS size,
|
||||
fld.created_at AS resource_created_at,
|
||||
fld.updated_at AS modified_at,
|
||||
fld.user_id AS owner_id,
|
||||
fld.drive_id AS drive_id,
|
||||
NULL::text AS blob_hash,
|
||||
fld.trashed_at AS trashed_at,
|
||||
@@ -401,7 +400,6 @@ impl TrashDbRepository {
|
||||
f.size::bigint AS size,
|
||||
f.created_at AS resource_created_at,
|
||||
f.updated_at AS modified_at,
|
||||
f.user_id AS owner_id,
|
||||
f.drive_id AS drive_id,
|
||||
f.blob_hash,
|
||||
f.trashed_at AS trashed_at,
|
||||
@@ -533,7 +531,7 @@ impl TrashDbRepository {
|
||||
SELECT
|
||||
r.resource_type, r.resource_id, r.name, r.parent_id,
|
||||
r.mime_type, r.size, r.resource_created_at, r.modified_at,
|
||||
r.owner_id, r.drive_id, r.trashed_at, r.deletion_date, r.resource_path,
|
||||
r.drive_id, r.trashed_at, r.deletion_date, r.resource_path,
|
||||
r.sort_str, r.type_order, r.folder_first
|
||||
FROM resources r
|
||||
{keyset}
|
||||
@@ -590,7 +588,6 @@ LIMIT $6"
|
||||
size,
|
||||
resource_created_at: row.get("resource_created_at"),
|
||||
modified_at: row.get("modified_at"),
|
||||
owner_id: row.try_get("owner_id").ok(),
|
||||
drive_id: row.get("drive_id"),
|
||||
blob_hash: row.try_get("blob_hash").ok(),
|
||||
trashed_at,
|
||||
|
||||
@@ -160,7 +160,7 @@ impl PathResolverService {
|
||||
name,
|
||||
res_path,
|
||||
parent_id,
|
||||
uid,
|
||||
_uid, // Post-D7: `user_id` column no longer flowed into the DTO.
|
||||
drive_id,
|
||||
created_at,
|
||||
modified_at,
|
||||
@@ -180,7 +180,6 @@ impl PathResolverService {
|
||||
name: name.clone(),
|
||||
path: res_path,
|
||||
parent_id,
|
||||
owner_id: uid,
|
||||
drive_id,
|
||||
created_at: created_at as u64,
|
||||
modified_at: modified_at as u64,
|
||||
@@ -215,7 +214,6 @@ impl PathResolverService {
|
||||
icon_special_class: Arc::from(icon_special_class_for(&name, &mime)),
|
||||
category: Arc::from(category_for(&name, &mime)),
|
||||
size_formatted: format_file_size(sz),
|
||||
owner_id: uid,
|
||||
sort_date: None,
|
||||
content_hash: hash,
|
||||
etag,
|
||||
|
||||
Reference in New Issue
Block a user