feat(drive): limit NC request by disk

This commit is contained in:
Edouard Vanbelle
2026-06-19 07:49:33 +02:00
parent aa155dffa0
commit 50fb34659e
28 changed files with 591 additions and 179 deletions
+39 -1
View File
@@ -29,6 +29,13 @@ pub struct Folder {
/// `None` only for legacy/stub folders; real folders always have an owner.
owner_id: Option<Uuid>,
/// Drive that owns this folder. Post-D0 every `storage.folders` row
/// has `drive_id NOT NULL` (M3 migration). Path-based lookups scope
/// by this axis (not by `user_id`, which is dropped in D7).
/// `Uuid::nil()` only for stub/legacy in-memory folders that never
/// touched the DB.
drive_id: Uuid,
/// Creation timestamp
created_at: u64,
@@ -56,6 +63,7 @@ impl Default for Folder {
path_string: "/".to_string(),
parent_id: None,
owner_id: None,
drive_id: Uuid::nil(),
created_at: 0,
modified_at: 0,
tree_modified_at: 0,
@@ -103,6 +111,11 @@ impl Folder {
path_string,
parent_id,
owner_id,
// In-memory constructor: callers that don't supply a
// drive_id are by definition stub/legacy paths (tests,
// pre-D0 fixtures, DTO round-trips). Real DB-backed
// folders flow through `with_timestamps_and_tree`.
drive_id: Uuid::nil(),
created_at: now,
modified_at: now,
tree_modified_at: now,
@@ -128,6 +141,7 @@ impl Folder {
storage_path,
parent_id,
None,
Uuid::nil(),
created_at,
modified_at,
modified_at,
@@ -154,6 +168,7 @@ impl Folder {
storage_path,
parent_id,
owner_id,
Uuid::nil(),
created_at,
modified_at,
modified_at,
@@ -162,7 +177,9 @@ impl Folder {
/// Full constructor used by the PG repository when reading rows.
/// `tree_modified_at` comes from the trigger-maintained column on
/// `storage.folders` and feeds [`Folder::etag`].
/// `storage.folders` and feeds [`Folder::etag`]. `drive_id` is the
/// post-D0 `storage.folders.drive_id NOT NULL` column — every
/// path-based lookup scopes by this axis.
#[allow(clippy::too_many_arguments)]
pub fn with_timestamps_and_tree(
id: String,
@@ -170,6 +187,7 @@ impl Folder {
storage_path: StoragePath,
parent_id: Option<String>,
owner_id: Option<Uuid>,
drive_id: Uuid,
created_at: u64,
modified_at: u64,
tree_modified_at: u64,
@@ -188,6 +206,7 @@ impl Folder {
path_string,
parent_id,
owner_id,
drive_id,
created_at,
modified_at,
tree_modified_at,
@@ -227,6 +246,13 @@ impl Folder {
self.owner_id
}
/// Drive that owns this folder. Path-based lookups scope by
/// this axis (post-D0 invariant: `storage.folders.drive_id`
/// is `NOT NULL`).
pub fn drive_id(&self) -> Uuid {
self.drive_id
}
/// Latest descendant-write timestamp. Statement-level Postgres
/// triggers enqueue every file/folder write into
/// `storage.tree_etag_dirty`; the background `TreeEtagFlushService`
@@ -314,6 +340,11 @@ impl Folder {
path_string: path,
parent_id,
owner_id: None,
// DTO round-trips lose drive_id (FolderDto carries it,
// but the legacy `from_dto` signature predates this
// change). Callers that need real scoping must reload
// through the repository.
drive_id: Uuid::nil(),
created_at,
modified_at,
tree_modified_at: modified_at,
@@ -353,6 +384,7 @@ impl Folder {
path_string: new_path_string,
parent_id: self.parent_id.clone(),
owner_id: self.owner_id,
drive_id: self.drive_id,
created_at: self.created_at,
modified_at: now,
// Renaming bumps both self and descendant rollup —
@@ -389,6 +421,7 @@ impl Folder {
path_string: new_path_string,
parent_id,
owner_id: self.owner_id,
drive_id: self.drive_id,
created_at: self.created_at,
modified_at: now,
tree_modified_at: now,
@@ -478,6 +511,7 @@ mod tests {
StoragePath::from_string("/folder"),
None,
None,
Uuid::nil(),
1_000,
2_000,
5_000,
@@ -499,6 +533,7 @@ mod tests {
StoragePath::from_string("/a"),
None,
None,
Uuid::nil(),
0,
0,
42,
@@ -510,6 +545,7 @@ mod tests {
StoragePath::from_string("/b"),
None,
None,
Uuid::nil(),
0,
0,
42,
@@ -532,6 +568,7 @@ mod tests {
StoragePath::from_string("/folder"),
None,
None,
Uuid::nil(),
1_000,
2_000,
3_000,
@@ -543,6 +580,7 @@ mod tests {
StoragePath::from_string("/folder"),
None,
None,
Uuid::nil(),
1_000,
2_000,
4_000,
+7 -2
View File
@@ -12,6 +12,7 @@ use std::path::PathBuf;
use bytes::Bytes;
use futures::Stream;
use uuid::Uuid;
use crate::common::errors::DomainError;
use crate::domain::entities::file::File;
@@ -49,8 +50,12 @@ pub trait FileReadRepository: Send + Sync + 'static {
/// Gets the logical storage path of a file.
async fn get_file_path(&self, id: &str) -> Result<StoragePath, DomainError>;
/// Gets the parent folder ID from a path (WebDAV).
async fn get_parent_folder_id(&self, path: &str) -> Result<String, DomainError>;
/// Gets the parent folder ID from a path (WebDAV), scoped to a drive.
///
/// Post-D0, `storage.folders.path` is unique only within a single
/// drive — the `drive_id` filter scopes the lookup.
async fn get_parent_folder_id(&self, path: &str, drive_id: Uuid)
-> Result<String, DomainError>;
}
// ─────────────────────────────────────────────────────
+16 -9
View File
@@ -28,17 +28,17 @@ pub trait FolderRepository: Send + Sync + 'static {
/// Gets a folder by its ID
async fn get_folder(&self, id: &str) -> Result<Folder, DomainError>;
/// Gets a folder by its storage path within the caller's tree.
/// Gets a folder by its storage path within a drive's tree.
///
/// Post-D0, `storage.folders.path` is no longer globally unique —
/// multiple users share root-folder names like `"Personal"`. The
/// `user_id` filter scopes the lookup to the caller's own folders
/// (the equivalent of the pre-D0 implicit user-namespacing that
/// came from `My Folder - <username>` paths).
/// Post-D0, `storage.folders.path` is unique only within a single
/// drive — root-folder names like `"Personal"` repeat across drives.
/// The `drive_id` filter scopes the lookup to a specific drive
/// (caller derives it from its protocol context: NC chroot, native
/// default-drive lookup, WOPI default-drive lookup).
async fn get_folder_by_path(
&self,
storage_path: &StoragePath,
user_id: Uuid,
drive_id: Uuid,
) -> Result<Folder, DomainError>;
/// Lists folders within a parent folder
@@ -87,8 +87,15 @@ pub trait FolderRepository: Send + Sync + 'static {
/// Deletes a folder
async fn delete_folder(&self, id: &str) -> Result<(), DomainError>;
/// Checks if a folder exists at the given path
async fn folder_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError>;
/// Checks if a folder exists at the given path within a drive.
///
/// Post-D0 `storage.folders.path` is unique only within a single
/// drive — the `drive_id` filter scopes the existence check.
async fn folder_exists(
&self,
storage_path: &StoragePath,
drive_id: Uuid,
) -> Result<bool, DomainError>;
/// Gets the path of a folder
async fn get_folder_path(&self, id: &str) -> Result<StoragePath, DomainError>;