feat(drive): start implementation of drive

- add storage.drives
    - prepare migration phase
    - add created_by and updated_by on storage.folders
This commit is contained in:
Edouard Vanbelle
2026-06-18 13:29:41 +02:00
parent 77545aee05
commit eab7a609b9
43 changed files with 2434 additions and 154 deletions
@@ -61,6 +61,7 @@ pub trait AuthorizationEngine: Send + Sync + 'static {
let (kind, id) = match resource {
Resource::Folder(id) => ("Folder", id),
Resource::File(id) => ("File", id),
Resource::Drive(id) => ("Drive", id),
};
// Audit-worthy: denials are the interesting signal. Routed
// through the `audit` tracing target so log aggregators can
+17 -5
View File
@@ -35,14 +35,26 @@ pub struct ContentHitDto {
/// holds an `Option<Arc<dyn ContentIndexPort>>` (the feature is toggleable).
#[async_trait]
pub trait ContentIndexPort: Send + Sync + 'static {
/// Search indexed file names + content for `query`, scoped to `user_id`.
/// Search indexed file names + content for `query`, scoped to the drives
/// the caller can read.
///
/// Returns up to `limit` hits sorted by BM25 score descending. Matching is
/// tokenized (not substring): exact terms, typo-tolerant fuzzy terms
/// (edit distance 1) and prefix expansion on the last query token.
/// The filter is applied as an `Occur::Must` set-membership clause on
/// the `drive_id` field — Tantivy's collector only ever sees documents
/// in one of the accessible drives, so counts, snippets, and
/// pagination cursors all reflect the filtered set (no anti-
/// enumeration leak — see `docs/plan/drive.md` §11). Pass the
/// caller's full accessible-drive set; the engine already expands
/// group-mediated drive grants before this is called.
///
/// An empty `accessible_drive_ids` returns no hits — same semantics
/// as "no drives, no search" (e.g. external users with grants only).
/// Returns up to `limit` hits sorted by BM25 score descending.
/// Matching is tokenized (not substring): exact terms, typo-tolerant
/// fuzzy terms (edit distance 1) and prefix expansion on the last
/// query token.
async fn search_content(
&self,
user_id: Uuid,
accessible_drive_ids: &[Uuid],
query: &str,
limit: usize,
) -> Result<Vec<ContentHitDto>, DomainError>;
+3
View File
@@ -85,9 +85,12 @@ pub trait FolderUseCase: Send + Sync + 'static {
async fn delete_folder_with_perms(&self, id: &str, caller_id: Uuid) -> Result<(), DomainError>;
/// Creates a root-level home folder for a user during registration.
/// `drive_id` is the user's personal drive; the wrapper folder stays
/// during the D0 dual-write window (retires in M2b later).
async fn create_home_folder(
&self,
user_id: Uuid,
drive_id: Uuid,
name: String,
) -> Result<FolderDto, DomainError>;