# Upload Architecture and API Flow This document explains the upload logic and different APIs used in the drive system and attachment service. ## Overview The upload system consists of multiple layers working together to handle file selection, upload processing, state management, and display transformation. ## Architecture Layers > **Important Note:** Both simple upload and multipart upload use the **exact same core logic**. The only difference is the delivery method - multipart upload is just a more efficient way to handle larger files using the same underlying patterns. ### 1. AttachmentService - File Selection Layer The `AttachmentService` handles the initial file selection and preparation: **Key Methods:** - `takePhoto()` - Camera capture using `react-native-image-picker` - `pickDocuments()` - File picker using `react-native-document-picker` - `pickImages()` - Image-specific picker (shortcut method) **Features:** - Permission handling (Android camera permissions) - File validation (10MB size limit) - Multiple file type support - Error handling and user cancellation ### 2. DriveApi - Upload Processing Layer The `DriveApi` provides multiple upload strategies: #### A. Simple Upload (`uploadFile`) - Uses `FormData` with `multipart/form-data` - Direct upload to `/file/upload` endpoint - Suitable for smaller files - Single API call #### B. Multipart Upload (Advanced) For larger files, the system uses a 3-step multipart upload process: 1. **Get Presigned URLs** (`getPresignedUrls`) 2. **Upload File Parts** (`uploadFilePart`) 3. **Finalize Upload** (`finalizeUpload`) #### C. File Access (`getSignedUrl`) - Generates temporary signed URLs for file access - Used by `attachmentTransformer.ts` for displaying files - Handles authentication and CORS issues ### 3. DriveStore - State Management Layer The `DriveStore` manages upload state and integrates with the API: **Features:** - Progress tracking (`setUploadProgress`) - Error handling - State management (loading, success, error) - Automatic file addition to store after successful upload ### 4. AttachmentTransformer - Display Layer Handles transformation of uploaded files for display: **Process:** 1. Determines media type from file extension 2. Gets signed URL using `DriveApi.getSignedUrl()` 3. Falls back to direct URL if signed URL fails 4. Returns display-ready `Attachment` object ## Upload Flow Diagram ```mermaid graph TD A[User Action<br/>Take Photo/Pick File] --> B[AttachmentService] B --> C{Camera or<br/>File Picker} C -->|Camera| D[launchCamera] C -->|Files| E[DocumentPicker.pick] D --> F[Create Attachment Object] E --> F F --> G[File Validation<br/>10MB limit] G -->|Valid| H[DriveApi.getInstance] G -->|Invalid| I[Return Error] H --> J{Upload Strategy} J -->|Small Files| K[Simple Upload<br/>uploadFile] J -->|Large Files| L[Multipart Upload] K --> M[FormData POST<br/>/file/upload<br/>Same DriveApi instance] L --> N[Step 1: getPresignedUrls<br/>Same DriveApi instance] N --> O[Step 2: uploadFilePart<br/>Same RNFS.readFile logic<br/>Same binary conversion] O --> P[Step 3: finalizeUpload<br/>Same DriveApi instance] M --> Q[DriveStore<br/>Same state management] P --> Q Q --> R[Same Progress Tracking<br/>Same Error Handling] R --> S[Add to Files Array] S --> T[AttachmentTransformer<br/>Same getSignedUrl logic] T --> U[Same Signed URL Process] U --> V[Return Display Format] style H fill:#e1f5fe style Q fill:#e1f5fe style T fill:#e1f5fe style O fill:#fff3e0 style M fill:#fff3e0 ``` ## API Endpoints ### Core Upload Endpoints | Endpoint | Method | Purpose | |----------|--------|---------| | `/file/upload` | POST | Simple file upload | | `/upload/get-presigned-urls` | GET | Get S3 presigned URLs for multipart | | `/uppy/sign-part/{uploadId}/{partNumber}` | POST | Sign individual upload parts | | `/upload/finalize` | POST | Complete multipart upload | | `/file/signed-url` | GET | Get signed URL for file access | ### File Management Endpoints | Endpoint | Method | Purpose | |----------|--------|---------| | `/file/find/{fileId}` | GET | Find file by ID | | `/files` | GET | List files with pagination | | `/file/{fileId}` | DELETE | Delete file | | `/file/{fileId}/share` | POST | Share file with users | | `/folder` | POST | Create folder | | `/file/{fileId}/trash` | PATCH | Move to trash | | `/file/{fileId}/restore` | PATCH | Restore from trash | ## Upload Strategies Comparison ### Simple Upload ```typescript // Single API call const formData = new FormData() formData.append("file", params.file) formData.append("name", params.name) const response = await this.apisauce.post("/file/upload", formData) ``` **Pros:** - Simple implementation - Single API call - Good for small files **Cons:** - Limited by server timeout - No progress tracking - Memory intensive for large files ### Multipart Upload (Same Core Logic) Even in multipart upload, the system uses the **same fundamental logic** as simple upload: ```typescript // Step 1: Get presigned URLs (same DriveApi instance) const presignedUrls = await DriveApi.getInstance().getPresignedUrls({ name, size }) // Step 2: Upload parts directly to S3 (same file handling) const parts = await Promise.all( fileParts.map(async (part, index) => { // Still uses same RNFS.readFile and binary conversion logic const result = await this.uploadFilePart(signedUrls[index], part.uri) return { ETag: result.etag, PartNumber: index + 1 } }) ) // Step 3: Finalize upload (same DriveApi instance) await DriveApi.getInstance().finalizeUpload({ fileKey, parts, fileId, ... }) ``` **Key Point:** Multipart upload is just a **different delivery method** for the same file data. The core logic remains identical: 1. **Same DriveApi instance** - Uses the same singleton pattern 2. **Same file reading** - Uses `RNFS.readFile(fileUri, "base64")` 3. **Same binary conversion** - Converts base64 to binary the same way 4. **Same authentication** - Uses same headers (`current-org-id`, `x-device-id`) 5. **Same error handling** - Uses same `getGeneralApiProblem` pattern 6. **Same state management** - Goes through same DriveStore flow **The only difference is WHERE the file goes:** - **Simple Upload**: File → Server → S3 - **Multipart Upload**: File → S3 directly (via presigned URLs) **Pros:** - Handles large files efficiently - Parallel upload of parts - Better error recovery - Progress tracking per part - **Same familiar API patterns** **Cons:** - More API calls required - Requires S3 integration - **But uses identical core logic** ## Shared Logic Components Both upload strategies share these identical components: ### 1. DriveApi Singleton Instance ```typescript // Both strategies use the same instance const driveApi = DriveApi.getInstance() ``` ### 2. File Reading Logic ```typescript // Both use identical RNFS file reading const fileContent = await RNFS.readFile(fileUri, "base64") const binaryString = atob(fileContent) const bytes = new Uint8Array(binaryString.length) for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i) } ``` ### 3. Authentication Headers ```typescript // Both use same headers automatically added by ApiBase headers: { "current-org-id": this.currentOrgId, "x-device-id": this.deviceId } ``` ### 4. Error Handling Pattern ```typescript // Both use identical error handling const problem = getGeneralApiProblem(response) if (problem) return problem ``` ### 5. State Management Flow ```typescript // Both go through same DriveStore flow this.setUploadProgress(25, "Uploading...") const result = await DriveApi.getInstance().uploadFile(params) this.setUploadProgress(100, "Upload complete!") ``` ### 6. File Display Logic ```typescript // Both use same signed URL logic for display const result = await driveApi.getSignedUrl(fileKey) ``` **The key insight:** Multipart upload is not a different system - it's the same system with a different delivery mechanism. All the core patterns, error handling, state management, and file processing remain identical. ## File Types and Validation ### Supported File Types **Images:** - JPG, JPEG, PNG, GIF, BMP, WebP **Documents:** - PDF, DOC, DOCX **General:** - Any file type (with 10MB limit) ### Validation Rules ```typescript validateAttachment(attachment: Attachment): { valid: boolean; error?: string } { // Check URI exists if (!attachment.uri) { return { valid: false, error: "Missing file URI" } } // Check name exists if (!attachment.name) { return { valid: false, error: "Missing file name" } } // Check file size (10MB limit) const maxSize = 10 * 1024 * 1024 // 10MB if (attachment.size && attachment.size > maxSize) { return { valid: false, error: "File too large (max 10MB)" } } return { valid: true } } ``` ## Security Features ### Authentication Headers - `current-org-id`: Organization context - `x-device-id`: Device identification ### Access Control - **Owner**: Full control (edit, delete, share) - **Editor**: Can edit and share - **Viewer**: Read-only access ### Signed URLs - Temporary access with expiration - Prevents unauthorized access - Handles CORS issues in mobile apps ## Error Handling ### Common Error Scenarios 1. **Permission Denied** ```typescript return { success: false, error: "Camera permission denied" } ``` 2. **File Too Large** ```typescript return { valid: false, error: "File too large (max 10MB)" } ``` 3. **Upload Failed** ```typescript return { kind: "unknown", temporary: true, error } ``` 4. **Network Issues** ```typescript return { kind: "unknown", temporary: true } ``` ## State Management Flow ```mermaid stateDiagram-v2 [*] --> Idle Idle --> Selecting: User selects file Selecting --> Validating: File selected Validating --> Valid: File valid Validating --> Invalid: File invalid Invalid --> [*]: Show error Valid --> Uploading: Start upload Uploading --> Progress: Upload in progress Progress --> Success: Upload complete Progress --> Failed: Upload failed Success --> [*]: Show success Failed --> [*]: Show error ``` ## Integration Points ### With Chat System - Files uploaded can be attached to messages - `conversationId` and `channelId` fields link files to conversations - Real-time updates via WebSocket events ### With Project System - Files can be organized by `projectId` - Folder structure with `parentId` relationships - Project-specific file sharing ### With User Management - User-specific quotas and limits - Organization-based access control - Sharing permissions per user ## Performance Considerations ### File Size Limits - **Simple Upload**: Limited by server timeout (~30 seconds) - **Multipart Upload**: No practical limit (handles GB+ files) ### Memory Usage - **Simple Upload**: Entire file loaded into memory - **Multipart Upload**: Only current part in memory ### Network Efficiency - **Simple Upload**: Single HTTP request - **Multipart Upload**: Multiple parallel requests ## Best Practices 1. **Use Simple Upload** for files < 10MB 2. **Use Multipart Upload** for files > 10MB 3. **Always validate** files before upload 4. **Handle errors gracefully** with user-friendly messages 5. **Show progress** for better user experience 6. **Clean up** temporary files after upload 7. **Use signed URLs** for file access to avoid CORS issues <style> section#notes-recommendations, section#notes-recommendations hr { display: none !important; } </style>