//! Chunked Upload Port - Application layer abstraction for resumable chunked uploads. //! //! This module defines the port (trait) and DTOs for chunked/resumable upload //! operations, keeping the application and interface layers independent of //! the specific upload implementation (TUS-like protocol, S3 multipart, etc.). 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; /// Minimum file size to use chunked upload (10 MB). pub const CHUNKED_UPLOAD_THRESHOLD: usize = 10 * 1024 * 1024; /// Response returned when a new upload session is created. #[derive(Debug, Clone, Serialize)] pub struct CreateUploadResponseDto { pub upload_id: String, pub chunk_size: usize, pub total_chunks: usize, pub expires_at: u64, } /// Response returned after a single chunk is uploaded. #[derive(Debug, Clone, Serialize)] pub struct ChunkUploadResponseDto { pub chunk_index: usize, pub bytes_received: u64, pub progress: f64, pub is_complete: bool, } /// Response for querying upload session status. #[derive(Debug, Clone, Serialize)] pub struct UploadStatusResponseDto { pub upload_id: String, pub filename: String, pub total_size: u64, pub bytes_received: u64, pub progress: f64, pub total_chunks: usize, pub completed_chunks: usize, pub pending_chunks: Vec, pub is_complete: bool, } /// Port for chunked/resumable upload operations. /// /// Implementations manage upload sessions, chunk storage, reassembly, /// and cleanup, while the application layer only interacts through /// this abstraction. pub trait ChunkedUploadPort: Send + Sync + 'static { /// Create a new upload session. /// /// Returns session metadata including the upload ID, chunk size, /// total number of chunks, and expiration timestamp. async fn create_session( &self, user_id: Uuid, filename: String, folder_id: Option, content_type: String, total_size: u64, chunk_size: Option, ) -> Result; /// Upload a single chunk. /// /// `checksum` is an optional MD5 hex string for integrity verification. async fn upload_chunk( &self, upload_id: &str, user_id: Uuid, chunk_index: usize, data: Bytes, checksum: Option, ) -> Result; /// Get the current status of an upload session. async fn get_status( &self, upload_id: &str, user_id: Uuid, ) -> Result; /// Assemble all chunks into the final file. /// /// Returns `(assembled_file_path, filename, folder_id, content_type, total_size, blake3_hash)`. /// The hash is computed during assembly (hash-on-write), eliminating a /// second sequential read of the assembled file. async fn complete_upload( &self, upload_id: &str, user_id: Uuid, ) -> Result<(PathBuf, String, Option, String, u64, String), DomainError>; /// Finalize upload: clean up the session and temporary files. 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: Uuid) -> Result<(), DomainError>; /// Check if a file size qualifies for chunked upload. fn should_use_chunked(&self, size: u64) -> bool; }