Files
Oxicloud/doc/trash-feature-summary.md
T

118 lines
5.1 KiB
Markdown
Raw Normal View History

# 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<PgPool>,
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**)