Files
Oxicloud/doc/trash-feature-summary.md
T
Dionisio f2d35ca792 feat: auto-persist JWT secret, remove setup token requirement
- JWT secret auto-generates and persists to <STORAGE_PATH>/.jwt_secret
- Remove setup token: first admin setup is open until system initialized
- Fix schema.sql: move CREATE EXTENSION pg_trgm/ltree to top
- Update login UI and auth.js to remove setup token fields
2026-03-05 22:12:53 +01:00

5.1 KiB
Executable File

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
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

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)