# 14 - Trash Feature Soft-delete for files and folders. Items go to a per-user trash bin instead of being permanently removed. Configurable retention period with automatic cleanup. ## Architecture Follows the hexagonal architecture: 1. **Domain Layer** (`/src/domain/`): - Entities: **TrashedItem** representing files and folders in the trash - Repository interfaces: **TrashRepository** defining trash management operations 2. **Application Layer** (`/src/application/`): - DTOs: **TrashedItemDto** for data transfer between layers - Ports: **TrashUseCase** defining available operations - Services: **TrashService** implementing the trash use cases 3. **Infrastructure Layer** (`/src/infrastructure/`): - Repositories: **TrashDbRepository** (PostgreSQL) — reads from `storage.trash_items` VIEW, manages soft-delete flags - Trash-related methods in file/folder repositories: `FileBlobWriteRepository::move_to_trash()`, `FolderDbRepository::move_to_trash()`, etc. - Services: **TrashCleanupService** for automatic cleanup of expired items 4. **Interface Layer** (`/src/interfaces/`): - API handlers: `trash_handler.rs` with HTTP endpoints for trash operations - Routes: updated `routes.rs` to include trash endpoints ## Storage Model Trash uses a **soft-delete** model in PostgreSQL: - Files and folders have `is_trashed` (BOOLEAN) and `trashed_at` (TIMESTAMPTZ) columns - When an item is trashed, `is_trashed` is set to `TRUE` and `trashed_at` records the timestamp - `original_parent_id` / `original_folder_id` stores the original location for restore - The `storage.trash_items` VIEW provides a unified list of all trashed items (files + folders) - No physical file movement occurs — blob content stays at `.blobs/{prefix}/{hash}.blob` - Permanent deletion removes the DB row and decrements the blob reference counter ```sql CREATE OR REPLACE VIEW storage.trash_items AS SELECT id, name, 'file' AS item_type, folder_id AS parent_id, user_id, size, mime_type, trashed_at, created_at FROM storage.files WHERE is_trashed = TRUE UNION ALL SELECT id, name, 'folder' AS item_type, parent_id, user_id, 0 AS size, NULL AS mime_type, trashed_at, created_at FROM storage.folders WHERE is_trashed = TRUE; ``` ## Key Features 1. **Soft Deletion** — files and folders are flagged as trashed, not immediately deleted 2. **Per-User Trash** — each user has an isolated trash bin (filtered by `user_id`) 3. **Retention Policy** — items auto-delete after a configurable period 4. **Restoration** — items can be restored to their original location via `original_parent_id`/`original_folder_id` 5. **Permanent Deletion** — items can be permanently deleted before retention expires (removes DB row + decrements blob ref) 6. **Empty Trash** — wipe everything in the trash at once ## API Endpoints - `GET /api/trash` or `GET /api/trash/` — list all items in the user's trash - `DELETE /api/trash/files/:id` — move a file to trash - `DELETE /api/trash/folders/:id` — move a folder to trash - `POST /api/trash/:id/restore` — restore an item to its original location - `DELETE /api/trash/:id` — permanently delete an item from trash - `DELETE /api/trash/empty` — empty the entire trash bin ## Implementation Details ### TrashDbRepository **File:** `src/infrastructure/repositories/pg/trash_db_repository.rs` ```rust pub struct TrashDbRepository { pool: Arc, retention_days: u32, } ``` Key methods: - `get_trash_items(user_id)` — SELECT from `storage.trash_items` WHERE `user_id = $1` - `clear_trash(user_id)` — DELETE from `storage.files` and `storage.folders` WHERE `is_trashed = TRUE AND user_id = $1` - `get_expired_items()` — finds items where `trashed_at + retention_days < NOW()` ### TrashService **File:** `src/application/services/trash_service.rs` Constructor: `TrashService::new(trash_repo, file_read, file_write, folder_repo, retention_days)` Orchestrates trash operations by delegating to the appropriate repository: - Moving a file to trash → `FileBlobWriteRepository::move_to_trash()` - Moving a folder to trash → `FolderDbRepository::move_to_trash()` - Permanent deletion → removes DB row + calls `DedupService::decrement_ref()` to clean blob if unreferenced ### TrashCleanupService **File:** `src/infrastructure/services/trash_cleanup_service.rs` Background job that runs every 24 hours to permanently delete items past the retention period. ## Testing 1. **Unit Tests** — testing **TrashService**: - Move files and folders to trash - Restore items from trash - Permanent deletion - Empty trash operation 2. **Integration Tests** — Python script hitting the API endpoints: - End-to-end testing of all trash operations - Verification of move, list, restore, and delete behavior ## Configuration - **OXICLOUD_ENABLE_TRASH**: enable/disable the trash feature via **FeaturesConfig** (default: true) - **OXICLOUD_TRASH_RETENTION_DAYS**: days to keep items before automatic deletion (default: 30, via **StorageConfig**)