perf: migrate all user/session/auth IDs from VARCHAR(36) to native UUID
- Schema: all ~15 VARCHAR(36) columns → UUID with DEFAULT gen_random_uuid() - Domain entities: User, Session, DeviceCode, AppPassword, Share → id: Uuid - DTOs: CurrentUser.id → Uuid (API boundary DTOs keep String for JSON) - Auth middleware: parse JWT claims.sub (String) → Uuid at boundary - All repository traits, port traits, service impls updated end-to-end - Handlers: pass Uuid by value (Copy, 16 bytes) instead of String refs - Settings chain: updated_by column → Uuid (was text, caused setup crash) - Removed ~650 lines of String↔Uuid conversion boilerplate - Eliminates per-request heap allocations for ID cloning - 16-byte binary comparison vs 36-byte string comparison in all queries - Native UUID indexing in PostgreSQL (btree on 16 bytes vs 36-char text) 85 files changed, 1090 insertions(+), 1739 deletions(-)
This commit is contained in:
@@ -3,6 +3,7 @@ use crate::domain::entities::app_password::AppPassword;
|
||||
use crate::domain::entities::device_code::DeviceCode;
|
||||
use crate::domain::entities::session::Session;
|
||||
use crate::domain::entities::user::User;
|
||||
use uuid::Uuid;
|
||||
|
||||
// ============================================================================
|
||||
// Cryptography Ports - Extracted from Domain to maintain Clean Architecture
|
||||
@@ -72,7 +73,7 @@ pub trait UserStoragePort: Send + Sync + 'static {
|
||||
async fn create_user(&self, user: User) -> Result<User, DomainError>;
|
||||
|
||||
/// Gets a user by ID
|
||||
async fn get_user_by_id(&self, id: &str) -> Result<User, DomainError>;
|
||||
async fn get_user_by_id(&self, id: Uuid) -> Result<User, DomainError>;
|
||||
|
||||
/// Gets a user by username
|
||||
async fn get_user_by_username(&self, username: &str) -> Result<User, DomainError>;
|
||||
@@ -86,7 +87,7 @@ pub trait UserStoragePort: Send + Sync + 'static {
|
||||
/// Updates only the storage usage of a user
|
||||
async fn update_storage_usage(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
usage_bytes: i64,
|
||||
) -> Result<(), DomainError>;
|
||||
|
||||
@@ -100,10 +101,10 @@ pub trait UserStoragePort: Send + Sync + 'static {
|
||||
async fn list_users_by_role(&self, role: &str) -> Result<Vec<User>, DomainError>;
|
||||
|
||||
/// Deletes a user by their ID
|
||||
async fn delete_user(&self, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn delete_user(&self, user_id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Changes a user's password
|
||||
async fn change_password(&self, user_id: &str, password_hash: &str) -> Result<(), DomainError>;
|
||||
async fn change_password(&self, user_id: Uuid, password_hash: &str) -> Result<(), DomainError>;
|
||||
|
||||
/// Finds a user by OIDC provider + subject pair
|
||||
async fn get_user_by_oidc_subject(
|
||||
@@ -113,15 +114,15 @@ pub trait UserStoragePort: Send + Sync + 'static {
|
||||
) -> Result<User, DomainError>;
|
||||
|
||||
/// Activates or deactivates a user
|
||||
async fn set_user_active_status(&self, user_id: &str, active: bool) -> Result<(), DomainError>;
|
||||
async fn set_user_active_status(&self, user_id: Uuid, active: bool) -> Result<(), DomainError>;
|
||||
|
||||
/// Changes a user's role
|
||||
async fn change_role(&self, user_id: &str, role: &str) -> Result<(), DomainError>;
|
||||
async fn change_role(&self, user_id: Uuid, role: &str) -> Result<(), DomainError>;
|
||||
|
||||
/// Updates a user's storage quota
|
||||
async fn update_storage_quota(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
quota_bytes: i64,
|
||||
) -> Result<(), DomainError>;
|
||||
|
||||
@@ -197,10 +198,10 @@ pub trait SessionStoragePort: Send + Sync + 'static {
|
||||
) -> Result<Session, DomainError>;
|
||||
|
||||
/// Revokes a specific session
|
||||
async fn revoke_session(&self, session_id: &str) -> Result<(), DomainError>;
|
||||
async fn revoke_session(&self, session_id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Revokes all sessions of a user
|
||||
async fn revoke_all_user_sessions(&self, user_id: &str) -> Result<u64, DomainError>;
|
||||
async fn revoke_all_user_sessions(&self, user_id: Uuid) -> Result<u64, DomainError>;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -224,10 +225,10 @@ pub trait DeviceCodeStoragePort: Send + Sync + 'static {
|
||||
async fn delete_expired(&self) -> Result<u64, DomainError>;
|
||||
|
||||
/// List authorized device codes for a user (for UI management)
|
||||
async fn list_by_user(&self, user_id: &str) -> Result<Vec<DeviceCode>, DomainError>;
|
||||
async fn list_by_user(&self, user_id: Uuid) -> Result<Vec<DeviceCode>, DomainError>;
|
||||
|
||||
/// Delete a specific device code by ID (revocation)
|
||||
async fn delete_by_id(&self, id: &str) -> Result<(), DomainError>;
|
||||
async fn delete_by_id(&self, id: Uuid) -> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -240,31 +241,31 @@ pub trait AppPasswordStoragePort: Send + Sync + 'static {
|
||||
async fn create(&self, app_password: AppPassword) -> Result<AppPassword, DomainError>;
|
||||
|
||||
/// Get all active (non-expired) app passwords for a user.
|
||||
async fn list_by_user(&self, user_id: &str) -> Result<Vec<AppPassword>, DomainError>;
|
||||
async fn list_by_user(&self, user_id: Uuid) -> Result<Vec<AppPassword>, DomainError>;
|
||||
|
||||
/// Get a specific app password by ID.
|
||||
async fn get_by_id(&self, id: &str) -> Result<AppPassword, DomainError>;
|
||||
async fn get_by_id(&self, id: Uuid) -> Result<AppPassword, DomainError>;
|
||||
|
||||
/// Get all active app passwords for a user ID (for Basic auth verification).
|
||||
/// This includes the password hash for verification.
|
||||
async fn get_active_by_user_id(&self, user_id: &str) -> Result<Vec<AppPassword>, DomainError>;
|
||||
async fn get_active_by_user_id(&self, user_id: Uuid) -> Result<Vec<AppPassword>, DomainError>;
|
||||
|
||||
/// Update the `last_used_at` timestamp after a successful authentication.
|
||||
async fn touch_last_used(&self, id: &str) -> Result<(), DomainError>;
|
||||
async fn touch_last_used(&self, id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Get active app passwords for a user filtered by token prefix (first 8 chars).
|
||||
/// More efficient than `get_active_by_user_id` when the password prefix is known.
|
||||
async fn get_active_by_user_prefix(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
prefix: &str,
|
||||
) -> Result<Vec<AppPassword>, DomainError>;
|
||||
|
||||
/// Deactivate (soft-delete) an app password, scoped to the owning user.
|
||||
async fn revoke(&self, id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn revoke(&self, id: Uuid, user_id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Delete an app password owned by a specific user. Returns true if found and deleted.
|
||||
async fn delete_by_user_and_id(&self, id: &str, user_id: &str) -> Result<bool, DomainError>;
|
||||
async fn delete_by_user_and_id(&self, id: Uuid, user_id: Uuid) -> Result<bool, DomainError>;
|
||||
|
||||
/// Hard-delete expired/revoked app passwords (cleanup).
|
||||
async fn delete_expired(&self) -> Result<u64, DomainError>;
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::application::dtos::calendar_dto::{
|
||||
};
|
||||
use crate::common::errors::DomainError;
|
||||
use chrono::{DateTime, Utc};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Port for external calendar storage mechanisms
|
||||
pub trait CalendarStoragePort: Send + Sync + 'static {
|
||||
@@ -11,7 +12,7 @@ pub trait CalendarStoragePort: Send + Sync + 'static {
|
||||
async fn create_calendar(
|
||||
&self,
|
||||
calendar: CreateCalendarDto,
|
||||
owner_id: &str,
|
||||
owner_id: Uuid,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn update_calendar(
|
||||
&self,
|
||||
@@ -22,11 +23,11 @@ pub trait CalendarStoragePort: Send + Sync + 'static {
|
||||
async fn get_calendar(&self, calendar_id: &str) -> Result<CalendarDto, DomainError>;
|
||||
async fn list_calendars_by_owner(
|
||||
&self,
|
||||
owner_id: &str,
|
||||
owner_id: Uuid,
|
||||
) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_calendars_shared_with_user(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_public_calendars(
|
||||
&self,
|
||||
@@ -36,20 +37,20 @@ pub trait CalendarStoragePort: Send + Sync + 'static {
|
||||
async fn check_calendar_access(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<bool, DomainError>;
|
||||
|
||||
// Calendar sharing
|
||||
async fn share_calendar(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
access_level: &str,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn remove_calendar_sharing(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn get_calendar_shares(
|
||||
&self,
|
||||
@@ -114,22 +115,22 @@ pub trait CalendarUseCase: Send + Sync + 'static {
|
||||
async fn create_calendar(
|
||||
&self,
|
||||
calendar: CreateCalendarDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn update_calendar(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
update: UpdateCalendarDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn delete_calendar(&self, calendar_id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn delete_calendar(&self, calendar_id: &str, user_id: Uuid) -> Result<(), DomainError>;
|
||||
async fn get_calendar(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<CalendarDto, DomainError>;
|
||||
async fn list_my_calendars(&self, user_id: &str) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_shared_calendars(&self, user_id: &str) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_my_calendars(&self, user_id: Uuid) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_shared_calendars(&self, user_id: Uuid) -> Result<Vec<CalendarDto>, DomainError>;
|
||||
async fn list_public_calendars(
|
||||
&self,
|
||||
limit: Option<i64>,
|
||||
@@ -140,57 +141,57 @@ pub trait CalendarUseCase: Send + Sync + 'static {
|
||||
async fn share_calendar(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
target_user_id: &str,
|
||||
target_user_id: Uuid,
|
||||
access_level: &str,
|
||||
caller_user_id: &str,
|
||||
caller_user_id: Uuid,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn remove_calendar_sharing(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
target_user_id: &str,
|
||||
caller_user_id: &str,
|
||||
target_user_id: Uuid,
|
||||
caller_user_id: Uuid,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn get_calendar_shares(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<(String, String)>, DomainError>;
|
||||
|
||||
// Event operations
|
||||
async fn create_event(
|
||||
&self,
|
||||
event: CreateEventDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn create_event_from_ical(
|
||||
&self,
|
||||
event: CreateEventICalDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn update_event(
|
||||
&self,
|
||||
event_id: &str,
|
||||
update: UpdateEventDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn delete_event(&self, event_id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn delete_event(&self, event_id: &str, user_id: Uuid) -> Result<(), DomainError>;
|
||||
async fn get_event(
|
||||
&self,
|
||||
event_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<CalendarEventDto, DomainError>;
|
||||
async fn list_events(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
limit: Option<i64>,
|
||||
offset: Option<i64>,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError>;
|
||||
async fn get_events_in_range(
|
||||
&self,
|
||||
calendar_id: &str,
|
||||
start: DateTime<Utc>,
|
||||
end: DateTime<Utc>,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<CalendarEventDto>, DomainError>;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::application::dtos::contact_dto::{
|
||||
GroupMembershipDto, UpdateContactDto, UpdateContactGroupDto,
|
||||
};
|
||||
use crate::common::errors::DomainError;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub type CardDavRepositoryError = DomainError;
|
||||
|
||||
@@ -24,16 +25,16 @@ pub trait AddressBookUseCase: Send + Sync + 'static {
|
||||
async fn delete_address_book(
|
||||
&self,
|
||||
address_book_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn get_address_book(
|
||||
&self,
|
||||
address_book_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<AddressBookDto, DomainError>;
|
||||
async fn list_user_address_books(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<AddressBookDto>, DomainError>;
|
||||
async fn list_public_address_books(&self) -> Result<Vec<AddressBookDto>, DomainError>;
|
||||
|
||||
@@ -41,17 +42,17 @@ pub trait AddressBookUseCase: Send + Sync + 'static {
|
||||
async fn share_address_book(
|
||||
&self,
|
||||
dto: ShareAddressBookDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn unshare_address_book(
|
||||
&self,
|
||||
dto: UnshareAddressBookDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn get_address_book_shares(
|
||||
&self,
|
||||
address_book_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<(String, bool)>, DomainError>;
|
||||
}
|
||||
|
||||
@@ -67,19 +68,19 @@ pub trait ContactUseCase: Send + Sync + 'static {
|
||||
contact_id: &str,
|
||||
update: UpdateContactDto,
|
||||
) -> Result<ContactDto, DomainError>;
|
||||
async fn delete_contact(&self, contact_id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn get_contact(&self, contact_id: &str, user_id: &str)
|
||||
async fn delete_contact(&self, contact_id: &str, user_id: Uuid) -> Result<(), DomainError>;
|
||||
async fn get_contact(&self, contact_id: &str, user_id: Uuid)
|
||||
-> Result<ContactDto, DomainError>;
|
||||
async fn list_contacts(
|
||||
&self,
|
||||
address_book_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<ContactDto>, DomainError>;
|
||||
async fn search_contacts(
|
||||
&self,
|
||||
address_book_id: &str,
|
||||
query: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<ContactDto>, DomainError>;
|
||||
|
||||
// Contact Group operations
|
||||
@@ -92,49 +93,49 @@ pub trait ContactUseCase: Send + Sync + 'static {
|
||||
group_id: &str,
|
||||
update: UpdateContactGroupDto,
|
||||
) -> Result<ContactGroupDto, DomainError>;
|
||||
async fn delete_group(&self, group_id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn delete_group(&self, group_id: &str, user_id: Uuid) -> Result<(), DomainError>;
|
||||
async fn get_group(
|
||||
&self,
|
||||
group_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<ContactGroupDto, DomainError>;
|
||||
async fn list_groups(
|
||||
&self,
|
||||
address_book_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<ContactGroupDto>, DomainError>;
|
||||
|
||||
// Group membership
|
||||
async fn add_contact_to_group(
|
||||
&self,
|
||||
dto: GroupMembershipDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn remove_contact_from_group(
|
||||
&self,
|
||||
dto: GroupMembershipDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<(), DomainError>;
|
||||
async fn list_contacts_in_group(
|
||||
&self,
|
||||
group_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<ContactDto>, DomainError>;
|
||||
async fn list_groups_for_contact(
|
||||
&self,
|
||||
contact_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<ContactGroupDto>, DomainError>;
|
||||
|
||||
// vCard operations
|
||||
async fn get_contact_vcard(
|
||||
&self,
|
||||
contact_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<String, DomainError>;
|
||||
async fn get_contacts_as_vcards(
|
||||
&self,
|
||||
address_book_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<(String, String)>, DomainError>;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::common::errors::DomainError;
|
||||
use bytes::Bytes;
|
||||
use serde::Serialize;
|
||||
use std::path::PathBuf;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Default chunk size (5 MB) — optimised for parallel transfers.
|
||||
pub const DEFAULT_CHUNK_SIZE: usize = 5 * 1024 * 1024;
|
||||
@@ -59,7 +60,7 @@ pub trait ChunkedUploadPort: Send + Sync + 'static {
|
||||
/// total number of chunks, and expiration timestamp.
|
||||
async fn create_session(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
filename: String,
|
||||
folder_id: Option<String>,
|
||||
content_type: String,
|
||||
@@ -73,7 +74,7 @@ pub trait ChunkedUploadPort: Send + Sync + 'static {
|
||||
async fn upload_chunk(
|
||||
&self,
|
||||
upload_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
chunk_index: usize,
|
||||
data: Bytes,
|
||||
checksum: Option<String>,
|
||||
@@ -83,7 +84,7 @@ pub trait ChunkedUploadPort: Send + Sync + 'static {
|
||||
async fn get_status(
|
||||
&self,
|
||||
upload_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<UploadStatusResponseDto, DomainError>;
|
||||
|
||||
/// Assemble all chunks into the final file.
|
||||
@@ -94,14 +95,14 @@ pub trait ChunkedUploadPort: Send + Sync + 'static {
|
||||
async fn complete_upload(
|
||||
&self,
|
||||
upload_id: &str,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<(PathBuf, String, Option<String>, String, u64, String), DomainError>;
|
||||
|
||||
/// Finalize upload: clean up the session and temporary files.
|
||||
async fn finalize_upload(&self, upload_id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn finalize_upload(&self, upload_id: &str, user_id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Cancel an upload and clean up all temporary data.
|
||||
async fn cancel_upload(&self, upload_id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn cancel_upload(&self, upload_id: &str, user_id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Check if a file size qualifies for chunked upload.
|
||||
fn should_use_chunked(&self, size: u64) -> bool;
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::dtos::favorites_dto::{BatchFavoritesResult, FavoriteItemDto};
|
||||
use crate::common::errors::Result;
|
||||
|
||||
/// Defines operations for managing user favorites
|
||||
pub trait FavoritesUseCase: Send + Sync {
|
||||
/// Get all favorites for a user
|
||||
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>>;
|
||||
async fn get_favorites(&self, user_id: Uuid) -> Result<Vec<FavoriteItemDto>>;
|
||||
|
||||
/// Add an item to user's favorites
|
||||
async fn add_to_favorites(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<()>;
|
||||
async fn add_to_favorites(&self, user_id: Uuid, item_id: &str, item_type: &str) -> Result<()>;
|
||||
|
||||
/// Remove an item from user's favorites
|
||||
async fn remove_from_favorites(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
item_id: &str,
|
||||
item_type: &str,
|
||||
) -> Result<bool>;
|
||||
|
||||
/// Check if an item is in user's favorites
|
||||
async fn is_favorite(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<bool>;
|
||||
async fn is_favorite(&self, user_id: Uuid, item_id: &str, item_type: &str) -> Result<bool>;
|
||||
|
||||
/// Add multiple items to favorites in a single transaction.
|
||||
/// Returns enriched favourites list so the client can replace its cache.
|
||||
async fn batch_add_to_favorites(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
items: &[(String, String)],
|
||||
) -> Result<BatchFavoritesResult>;
|
||||
|
||||
@@ -34,7 +36,7 @@ pub trait FavoritesUseCase: Send + Sync {
|
||||
/// Returns the set of item_ids that are favorites.
|
||||
async fn batch_check_favorites(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
item_ids: &[(&str, &str)], // (item_id, item_type) pairs
|
||||
) -> Result<HashSet<String>>;
|
||||
}
|
||||
@@ -50,26 +52,26 @@ pub trait FavoritesUseCase: Send + Sync {
|
||||
/// lives in `infrastructure::repositories::pg`.
|
||||
pub trait FavoritesRepositoryPort: Send + Sync + 'static {
|
||||
/// Gets all favorites for a user.
|
||||
async fn get_favorites(&self, user_id: &str) -> Result<Vec<FavoriteItemDto>>;
|
||||
async fn get_favorites(&self, user_id: Uuid) -> Result<Vec<FavoriteItemDto>>;
|
||||
|
||||
/// Adds an item to favorites. Returns `Ok(())` if it already existed (idempotent).
|
||||
async fn add_favorite(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<()>;
|
||||
async fn add_favorite(&self, user_id: Uuid, item_id: &str, item_type: &str) -> Result<()>;
|
||||
|
||||
/// Removes an item from favorites. Returns `true` if it existed.
|
||||
async fn remove_favorite(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<bool>;
|
||||
async fn remove_favorite(&self, user_id: Uuid, item_id: &str, item_type: &str) -> Result<bool>;
|
||||
|
||||
/// Checks if an item is in favorites.
|
||||
async fn is_favorite(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<bool>;
|
||||
async fn is_favorite(&self, user_id: Uuid, item_id: &str, item_type: &str) -> Result<bool>;
|
||||
|
||||
/// Insert multiple items in a single transaction.
|
||||
/// Returns the number of rows actually inserted (ignoring duplicates).
|
||||
async fn add_favorites_batch(&self, user_id: &str, items: &[(String, String)]) -> Result<u64>;
|
||||
async fn add_favorites_batch(&self, user_id: Uuid, items: &[(String, String)]) -> Result<u64>;
|
||||
|
||||
/// Check which of the given item IDs are favorites for this user.
|
||||
/// Returns the set of item_ids that are favorites.
|
||||
async fn batch_check_favorites(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
item_ids: &[(&str, &str)], // (item_id, item_type) pairs
|
||||
) -> Result<HashSet<String>>;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use futures::Stream;
|
||||
use std::path::Path;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::dtos::file_dto::FileDto;
|
||||
use crate::application::ports::storage_ports::CopyFolderTreeResult;
|
||||
@@ -116,7 +117,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
///
|
||||
/// Returns `NotFound` if the file does not exist **or** belongs to
|
||||
/// another user. All user-facing handlers should use this method.
|
||||
async fn get_file_owned(&self, id: &str, caller_id: &str) -> Result<FileDto, DomainError>;
|
||||
async fn get_file_owned(&self, id: &str, caller_id: Uuid) -> Result<FileDto, DomainError>;
|
||||
|
||||
/// Gets a file by its path (for WebDAV)
|
||||
async fn get_file_by_path(&self, path: &str) -> Result<FileDto, DomainError>;
|
||||
@@ -131,7 +132,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
async fn list_files_owned(
|
||||
&self,
|
||||
folder_id: Option<&str>,
|
||||
owner_id: &str,
|
||||
owner_id: Uuid,
|
||||
) -> Result<Vec<FileDto>, DomainError>;
|
||||
|
||||
/// Gets file content as a stream (for large files)
|
||||
@@ -144,7 +145,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
async fn get_file_stream_owned(
|
||||
&self,
|
||||
id: &str,
|
||||
caller_id: &str,
|
||||
caller_id: Uuid,
|
||||
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError>;
|
||||
|
||||
/// Optimized multi-tier download.
|
||||
@@ -166,7 +167,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
async fn get_file_optimized_owned(
|
||||
&self,
|
||||
id: &str,
|
||||
caller_id: &str,
|
||||
caller_id: Uuid,
|
||||
accept_webp: bool,
|
||||
prefer_original: bool,
|
||||
) -> Result<(FileDto, OptimizedFileContent), DomainError>;
|
||||
@@ -198,7 +199,7 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
async fn get_file_range_stream_owned(
|
||||
&self,
|
||||
id: &str,
|
||||
caller_id: &str,
|
||||
caller_id: Uuid,
|
||||
start: u64,
|
||||
end: Option<u64>,
|
||||
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError>;
|
||||
@@ -238,14 +239,15 @@ pub trait FileRetrievalUseCase: Send + Sync + 'static {
|
||||
async fn list_files_batch_for_owner(
|
||||
&self,
|
||||
folder_id: Option<&str>,
|
||||
owner_id: &str,
|
||||
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_id))
|
||||
.filter(|f| f.owner_id.as_deref().is_some_and(|o| o == owner_str))
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
@@ -267,7 +269,7 @@ pub trait FileManagementUseCase: Send + Sync + 'static {
|
||||
async fn move_file_owned(
|
||||
&self,
|
||||
file_id: &str,
|
||||
caller_id: &str,
|
||||
caller_id: Uuid,
|
||||
folder_id: Option<String>,
|
||||
) -> Result<FileDto, DomainError>;
|
||||
|
||||
@@ -282,7 +284,7 @@ pub trait FileManagementUseCase: Send + Sync + 'static {
|
||||
async fn copy_file_owned(
|
||||
&self,
|
||||
file_id: &str,
|
||||
caller_id: &str,
|
||||
caller_id: Uuid,
|
||||
target_folder_id: Option<String>,
|
||||
) -> Result<FileDto, DomainError>;
|
||||
|
||||
@@ -293,7 +295,7 @@ pub trait FileManagementUseCase: Send + Sync + 'static {
|
||||
async fn rename_file_owned(
|
||||
&self,
|
||||
file_id: &str,
|
||||
caller_id: &str,
|
||||
caller_id: Uuid,
|
||||
new_name: &str,
|
||||
) -> Result<FileDto, DomainError>;
|
||||
|
||||
@@ -301,7 +303,7 @@ pub trait FileManagementUseCase: Send + Sync + 'static {
|
||||
async fn delete_file(&self, id: &str) -> Result<(), DomainError>;
|
||||
|
||||
/// Deletes a file, enforcing that `caller_id` is the owner.
|
||||
async fn delete_file_owned(&self, id: &str, caller_id: &str) -> Result<(), DomainError>;
|
||||
async fn delete_file_owned(&self, id: &str, caller_id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Smart delete: trash-first with dedup reference cleanup.
|
||||
///
|
||||
@@ -310,7 +312,7 @@ pub trait FileManagementUseCase: Send + Sync + 'static {
|
||||
/// 3. Decrements the dedup reference count for the content hash.
|
||||
///
|
||||
/// Returns `Ok(true)` when trashed, `Ok(false)` when permanently deleted.
|
||||
async fn delete_with_cleanup(&self, id: &str, user_id: &str) -> Result<bool, DomainError>;
|
||||
async fn delete_with_cleanup(&self, id: &str, user_id: Uuid) -> Result<bool, DomainError>;
|
||||
|
||||
/// Copies an entire folder subtree atomically (WebDAV COPY Depth: infinity).
|
||||
///
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::dtos::folder_dto::{
|
||||
CreateFolderDto, FolderDto, MoveFolderDto, RenameFolderDto,
|
||||
};
|
||||
@@ -20,7 +22,7 @@ pub trait FolderUseCase: Send + Sync + 'static {
|
||||
///
|
||||
/// Returns `NotFound` if the folder does not exist **or** belongs to
|
||||
/// another user. All user-facing handlers should use this method.
|
||||
async fn get_folder_owned(&self, id: &str, caller_id: &str) -> Result<FolderDto, DomainError>;
|
||||
async fn get_folder_owned(&self, id: &str, caller_id: Uuid) -> Result<FolderDto, DomainError>;
|
||||
|
||||
/// Gets a folder by its path
|
||||
async fn get_folder_by_path(&self, path: &str) -> Result<FolderDto, DomainError>;
|
||||
@@ -33,7 +35,7 @@ pub trait FolderUseCase: Send + Sync + 'static {
|
||||
async fn list_folders_for_owner(
|
||||
&self,
|
||||
parent_id: Option<&str>,
|
||||
owner_id: &str,
|
||||
owner_id: Uuid,
|
||||
) -> Result<Vec<FolderDto>, DomainError>;
|
||||
|
||||
/// Lists folders with pagination
|
||||
@@ -47,7 +49,7 @@ pub trait FolderUseCase: Send + Sync + 'static {
|
||||
async fn list_folders_for_owner_paginated(
|
||||
&self,
|
||||
parent_id: Option<&str>,
|
||||
owner_id: &str,
|
||||
owner_id: Uuid,
|
||||
pagination: &crate::application::dtos::pagination::PaginationRequestDto,
|
||||
) -> Result<crate::application::dtos::pagination::PaginatedResponseDto<FolderDto>, DomainError>;
|
||||
|
||||
@@ -56,7 +58,7 @@ pub trait FolderUseCase: Send + Sync + 'static {
|
||||
&self,
|
||||
id: &str,
|
||||
dto: RenameFolderDto,
|
||||
caller_id: &str,
|
||||
caller_id: Uuid,
|
||||
) -> Result<FolderDto, DomainError>;
|
||||
|
||||
/// Moves a folder to another parent (ownership verified against caller_id)
|
||||
@@ -64,16 +66,16 @@ pub trait FolderUseCase: Send + Sync + 'static {
|
||||
&self,
|
||||
id: &str,
|
||||
dto: MoveFolderDto,
|
||||
caller_id: &str,
|
||||
caller_id: Uuid,
|
||||
) -> Result<FolderDto, DomainError>;
|
||||
|
||||
/// Deletes a folder (ownership verified against caller_id)
|
||||
async fn delete_folder(&self, id: &str, caller_id: &str) -> Result<(), DomainError>;
|
||||
async fn delete_folder(&self, id: &str, caller_id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Creates a root-level home folder for a user during registration.
|
||||
async fn create_home_folder(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
name: String,
|
||||
) -> Result<FolderDto, DomainError>;
|
||||
|
||||
@@ -103,7 +105,7 @@ pub trait SearchUseCase: Send + Sync + 'static {
|
||||
async fn search(
|
||||
&self,
|
||||
criteria: SearchCriteriaDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Arc<SearchResultsDto>, DomainError>;
|
||||
|
||||
/// Returns quick suggestions for autocomplete (lightweight, fast).
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::dtos::recent_dto::RecentItemDto;
|
||||
use crate::common::errors::Result;
|
||||
|
||||
@@ -6,24 +8,24 @@ pub trait RecentItemsUseCase: Send + Sync {
|
||||
/// Get all recent items for a user
|
||||
async fn get_recent_items(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
limit: Option<i32>,
|
||||
) -> Result<Vec<RecentItemDto>>;
|
||||
|
||||
/// Record access to an item
|
||||
async fn record_item_access(&self, user_id: &str, item_id: &str, item_type: &str)
|
||||
async fn record_item_access(&self, user_id: Uuid, item_id: &str, item_type: &str)
|
||||
-> Result<()>;
|
||||
|
||||
/// Remove an item from recents
|
||||
async fn remove_from_recent(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
item_id: &str,
|
||||
item_type: &str,
|
||||
) -> Result<bool>;
|
||||
|
||||
/// Clear the entire recent items list
|
||||
async fn clear_recent_items(&self, user_id: &str) -> Result<()>;
|
||||
async fn clear_recent_items(&self, user_id: Uuid) -> Result<()>;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────
|
||||
@@ -36,17 +38,17 @@ pub trait RecentItemsUseCase: Send + Sync {
|
||||
/// `RecentService` does not depend directly on `PgPool`.
|
||||
pub trait RecentItemsRepositoryPort: Send + Sync + 'static {
|
||||
/// Gets the latest recent items for a user (ordered by date desc).
|
||||
async fn get_recent_items(&self, user_id: &str, limit: i32) -> Result<Vec<RecentItemDto>>;
|
||||
async fn get_recent_items(&self, user_id: Uuid, limit: i32) -> Result<Vec<RecentItemDto>>;
|
||||
|
||||
/// Records/updates access to an item (upsert by user+item+type).
|
||||
async fn upsert_access(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<()>;
|
||||
async fn upsert_access(&self, user_id: Uuid, item_id: &str, item_type: &str) -> Result<()>;
|
||||
|
||||
/// Removes an item from recents. Returns `true` if it existed.
|
||||
async fn remove_item(&self, user_id: &str, item_id: &str, item_type: &str) -> Result<bool>;
|
||||
async fn remove_item(&self, user_id: Uuid, item_id: &str, item_type: &str) -> Result<bool>;
|
||||
|
||||
/// Removes all recent items for a user.
|
||||
async fn clear_all(&self, user_id: &str) -> Result<()>;
|
||||
async fn clear_all(&self, user_id: Uuid) -> Result<()>;
|
||||
|
||||
/// Removes items exceeding `max_items` (the oldest ones).
|
||||
async fn prune(&self, user_id: &str, max_items: i32) -> Result<()>;
|
||||
async fn prune(&self, user_id: Uuid, max_items: i32) -> Result<()>;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
application::dtos::{
|
||||
pagination::PaginatedResponseDto,
|
||||
@@ -11,12 +13,12 @@ pub trait ShareUseCase: Send + Sync + 'static {
|
||||
/// Create a new shared link for a file or folder
|
||||
async fn create_shared_link(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
dto: CreateShareDto,
|
||||
) -> Result<ShareDto, DomainError>;
|
||||
|
||||
/// Get a shared link by its ID (ownership-verified)
|
||||
async fn get_shared_link(&self, id: &str, requester_id: &str) -> Result<ShareDto, DomainError>;
|
||||
async fn get_shared_link(&self, id: Uuid, requester_id: Uuid) -> Result<ShareDto, DomainError>;
|
||||
|
||||
/// Get a shared link by its token (for access by non-users)
|
||||
async fn get_shared_link_by_token(&self, token: &str) -> Result<ShareDto, DomainError>;
|
||||
@@ -26,24 +28,24 @@ pub trait ShareUseCase: Send + Sync + 'static {
|
||||
&self,
|
||||
item_id: &str,
|
||||
item_type: &ShareItemType,
|
||||
requester_id: &str,
|
||||
requester_id: Uuid,
|
||||
) -> Result<Vec<ShareDto>, DomainError>;
|
||||
|
||||
/// Update a shared link (ownership-verified)
|
||||
async fn update_shared_link(
|
||||
&self,
|
||||
id: &str,
|
||||
requester_id: &str,
|
||||
id: Uuid,
|
||||
requester_id: Uuid,
|
||||
dto: UpdateShareDto,
|
||||
) -> Result<ShareDto, DomainError>;
|
||||
|
||||
/// Delete a shared link (ownership-verified)
|
||||
async fn delete_shared_link(&self, id: &str, requester_id: &str) -> Result<(), DomainError>;
|
||||
async fn delete_shared_link(&self, id: Uuid, requester_id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Get all shared links created by a specific user
|
||||
async fn get_user_shared_links(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
page: usize,
|
||||
per_page: usize,
|
||||
) -> Result<PaginatedResponseDto<ShareDto>, DomainError>;
|
||||
@@ -77,19 +79,19 @@ pub trait ShareStoragePort: Send + Sync + 'static {
|
||||
/// (prevents share-ID enumeration).
|
||||
async fn find_share_by_id_for_user(
|
||||
&self,
|
||||
id: &str,
|
||||
user_id: &str,
|
||||
id: Uuid,
|
||||
user_id: Uuid,
|
||||
) -> Result<crate::domain::entities::share::Share, DomainError>;
|
||||
|
||||
/// Delete a share only if it belongs to the given user.
|
||||
async fn delete_share_for_user(&self, id: &str, user_id: &str) -> Result<(), DomainError>;
|
||||
async fn delete_share_for_user(&self, id: Uuid, user_id: Uuid) -> Result<(), DomainError>;
|
||||
|
||||
/// Find shares for a specific item that belong to the given user.
|
||||
async fn find_shares_by_item_for_user(
|
||||
&self,
|
||||
item_id: &str,
|
||||
item_type: &ShareItemType,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<Vec<crate::domain::entities::share::Share>, DomainError>;
|
||||
|
||||
async fn update_share(
|
||||
@@ -99,7 +101,7 @@ pub trait ShareStoragePort: Send + Sync + 'static {
|
||||
|
||||
async fn find_shares_by_user(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
offset: usize,
|
||||
limit: usize,
|
||||
) -> Result<(Vec<crate::domain::entities::share::Share>, usize), DomainError>;
|
||||
|
||||
@@ -3,6 +3,7 @@ use futures::Stream;
|
||||
use serde_json::Value;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::dtos::search_dto::SearchCriteriaDto;
|
||||
use crate::common::errors::DomainError;
|
||||
@@ -33,13 +34,13 @@ pub trait FileReadPort: Send + Sync + 'static {
|
||||
/// Returns `NotFound` if the file does not exist **or** belongs to a
|
||||
/// different user. This is the primary IDOR-safe accessor — handlers
|
||||
/// serving end-user requests should always prefer this over `get_file`.
|
||||
async fn get_file_for_owner(&self, id: &str, owner_id: &str) -> Result<File, DomainError>;
|
||||
async fn get_file_for_owner(&self, id: &str, owner_id: Uuid) -> Result<File, DomainError>;
|
||||
|
||||
/// Verifies that the file identified by `id` belongs to `owner_id`.
|
||||
///
|
||||
/// Returns `Ok(())` on success or `NotFound` when the file does not
|
||||
/// exist or belongs to another user.
|
||||
async fn verify_file_owner(&self, id: &str, owner_id: &str) -> Result<(), DomainError> {
|
||||
async fn verify_file_owner(&self, id: &str, owner_id: Uuid) -> Result<(), DomainError> {
|
||||
self.get_file_for_owner(id, owner_id).await.map(|_| ())
|
||||
}
|
||||
|
||||
@@ -53,12 +54,13 @@ pub trait FileReadPort: Send + Sync + 'static {
|
||||
async fn list_files_for_owner(
|
||||
&self,
|
||||
folder_id: Option<&str>,
|
||||
owner_id: &str,
|
||||
owner_id: Uuid,
|
||||
) -> Result<Vec<File>, DomainError> {
|
||||
let all = self.list_files(folder_id).await?;
|
||||
let owner_str = owner_id.to_string();
|
||||
Ok(all
|
||||
.into_iter()
|
||||
.filter(|f| f.owner_id().is_some_and(|o| o == owner_id))
|
||||
.filter(|f| f.owner_id().is_some_and(|o| o == owner_str))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -134,15 +136,16 @@ pub trait FileReadPort: Send + Sync + 'static {
|
||||
async fn list_files_batch_for_owner(
|
||||
&self,
|
||||
folder_id: Option<&str>,
|
||||
owner_id: &str,
|
||||
owner_id: Uuid,
|
||||
offset: i64,
|
||||
limit: i64,
|
||||
) -> Result<Vec<File>, DomainError> {
|
||||
// Default: filter in-memory (repos should override with SQL)
|
||||
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().is_some_and(|o| o == owner_id))
|
||||
.filter(|f| f.owner_id().is_some_and(|o| o == owner_str))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -175,7 +178,7 @@ pub trait FileReadPort: Send + Sync + 'static {
|
||||
&self,
|
||||
folder_id: Option<&str>,
|
||||
criteria: &SearchCriteriaDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<(Vec<File>, usize), DomainError>;
|
||||
|
||||
/// Search files recursively in a folder subtree using ltree.
|
||||
@@ -190,7 +193,7 @@ pub trait FileReadPort: Send + Sync + 'static {
|
||||
&self,
|
||||
root_folder_id: Option<&str>,
|
||||
criteria: &SearchCriteriaDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<(Vec<File>, usize), DomainError> {
|
||||
// Default: delegate to paginated search (non-recursive fallback)
|
||||
self.search_files_paginated(root_folder_id, criteria, user_id)
|
||||
@@ -204,7 +207,7 @@ pub trait FileReadPort: Send + Sync + 'static {
|
||||
&self,
|
||||
folder_id: Option<&str>,
|
||||
criteria: &SearchCriteriaDto,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
) -> Result<usize, DomainError>;
|
||||
|
||||
/// Return up to `limit` files whose name contains `query` (case-insensitive).
|
||||
@@ -359,7 +362,7 @@ pub trait FileWritePort: Send + Sync + 'static {
|
||||
/// Secondary port for storage usage management
|
||||
pub trait StorageUsagePort: Send + Sync + 'static {
|
||||
/// Updates storage usage statistics for a user
|
||||
async fn update_user_storage_usage(&self, user_id: &str) -> Result<i64, DomainError>;
|
||||
async fn update_user_storage_usage(&self, user_id: Uuid) -> Result<i64, DomainError>;
|
||||
|
||||
/// Updates storage usage statistics for a user, looked up by username
|
||||
async fn update_user_storage_usage_by_username(
|
||||
@@ -375,12 +378,12 @@ pub trait StorageUsagePort: Send + Sync + 'static {
|
||||
/// descriptive message otherwise.
|
||||
async fn check_storage_quota(
|
||||
&self,
|
||||
user_id: &str,
|
||||
user_id: Uuid,
|
||||
additional_bytes: u64,
|
||||
) -> Result<(), DomainError>;
|
||||
|
||||
/// Returns (used_bytes, quota_bytes) for a user.
|
||||
async fn get_user_storage_info(&self, user_id: &str) -> Result<(i64, i64), DomainError>;
|
||||
async fn get_user_storage_info(&self, user_id: Uuid) -> Result<(i64, i64), DomainError>;
|
||||
}
|
||||
|
||||
/// Generic storage service interface for calendar and contact services
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::dtos::trash_dto::TrashedItemDto;
|
||||
use crate::common::errors::Result;
|
||||
|
||||
/// Port for trash-related use cases
|
||||
pub trait TrashUseCase: Send + Sync {
|
||||
/// List items in the user's trash
|
||||
async fn get_trash_items(&self, user_id: &str) -> Result<Vec<TrashedItemDto>>;
|
||||
async fn get_trash_items(&self, user_id: Uuid) -> Result<Vec<TrashedItemDto>>;
|
||||
|
||||
/// Move a file or folder to trash
|
||||
async fn move_to_trash(&self, item_id: &str, item_type: &str, user_id: &str) -> Result<()>;
|
||||
async fn move_to_trash(&self, item_id: &str, item_type: &str, user_id: Uuid) -> Result<()>;
|
||||
|
||||
/// Restore an item from trash to its original location
|
||||
async fn restore_item(&self, trash_id: &str, user_id: &str) -> Result<()>;
|
||||
async fn restore_item(&self, trash_id: &str, user_id: Uuid) -> Result<()>;
|
||||
|
||||
/// Permanently delete an item from trash
|
||||
async fn delete_permanently(&self, trash_id: &str, user_id: &str) -> Result<()>;
|
||||
async fn delete_permanently(&self, trash_id: &str, user_id: Uuid) -> Result<()>;
|
||||
|
||||
/// Empty the trash for a specific user
|
||||
async fn empty_trash(&self, user_id: &str) -> Result<()>;
|
||||
async fn empty_trash(&self, user_id: Uuid) -> Result<()>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user