Appearance
Admin Upload & External Upload ​
Overview ​
This document covers two distinct upload systems in the Admin Frontend application:
Admin Upload: Comprehensive file upload system for authenticated workspace users, supporting direct browser uploads, chunked/multipart uploads for large files, upload queue management, progress tracking, error handling, and retry logic.
External Upload Integration: Guest user upload system with OTP authentication, allowing external users to upload files to workspaces with limited access and controlled permissions.
Both systems handle file validation, progress tracking, error handling, and automatic thumbnail generation.
Architecture ​
Admin Upload Methods ​
- Direct Upload: Small files uploaded directly via FormData
- Chunked/Multipart Upload: Large files split into chunks and uploaded in parts to S3
- Bulk Upload: Multiple files uploaded simultaneously with queue management
- Version Upload: Upload new versions of existing assets
External Upload Methods ​
- External Upload: Guest user uploads with OTP authentication
- Chunked External Upload: Large files for guest users using chunked upload
File Structure ​
Admin Upload Files ​
JavaScript Files (.js) ​
mixins/upload-common.js- Common upload functionality mixin- File validation
- Upload initialization
- Progress tracking helpers
- Error handling utilities
mixins/uploadQueue.js- Upload queue management- Queue operations (add, remove, pause, resume)
- Concurrent upload limits
- Queue state management
- Retry logic
api/s3.js- S3 upload API integration- Multipart upload initiation
- Presigned URL generation
- Upload completion
- Direct upload endpoints
api/index.js- General API (may contain upload endpoints)- Asset creation endpoints
- Upload status endpoints
utils/index.js- Utility functions (may contain upload helpers)- File type detection
- File size formatting
- Upload helpers
store/dam.js- DAM store (upload state management)- Upload queue state
- Upload progress state
- Upload history
Vue Component Files (.vue) ​
components/dam/AssetUpload.vue- Main asset upload component- File selection interface
- Drag and drop support
- Upload queue display
- Progress indicators
components/dam/upload-chunk.vue- Chunked upload component- File chunking logic
- Chunk upload coordination
- Progress tracking per chunk
- Error recovery
components/dam/Uploaded/UploadedListItem.vue- Uploaded item list component- Display uploaded files
- Upload status indicators
- Action buttons (retry, cancel, remove)
components/dam/Dialogs/miniUploadBackdrop.vue- Mini upload backdrop dialog- Modal backdrop for upload dialog
- Overlay styling
components/dam/Dialogs/miniUploadDialog.vue- Mini upload dialog- Compact upload interface
- Quick file selection
- Minimal progress display
components/dam/Dialogs/VersionUploadBackdrop.vue- Version upload backdrop- Modal for version uploads
- Version-specific upload UI
components/dam/SkeletonLoaders/UploadedTableListSkeleton.vue- Upload skeleton loader- Loading state for upload list
- Skeleton UI during data fetch
pages/_workspace_id/dam/upload.vue- Upload page- Main upload interface
- File selection
- Upload queue management
- Upload history
pages/_workspace_id/dam/uploaded.vue- Uploaded assets page- List of uploaded assets
- Upload status tracking
- Asset management
SVG Icon Components (Upload-related) ​
components/svg/CollageUploadIcon.vue- Standard upload iconcomponents/svg/CollageUploadMediumIcon.vue- Medium size upload iconcomponents/svg/CollageUploadLargeIcon.vue- Large size upload iconcomponents/svg/CollageUploadExtraLargeIcon.vue- Extra large upload iconcomponents/svg/CollageUploadCloudIcon.vue- Cloud upload icon
External Upload Files ​
JavaScript Files (.js) ​
mixins/external-uploads.js- External upload functionality mixin- Guest user upload handling
- OTP verification integration
- External upload queue management
- Guest-specific validation
middleware/external-upload-auth.js- External upload authentication middleware- Guest user authentication
- Token validation
- Access control
middleware/external-guest-redirect.js- External guest redirect middleware- Redirect logic for guest users
- Access verification
- Route protection
middleware/external-otp-verify.js- External OTP verification middleware- OTP code verification
- Session management
- Access grant
Vue Component Files (.vue) ​
components/dam/upload-chunk-external.vue- External chunked upload component- Guest user chunked uploads
- OTP-verified upload flow
- External upload progress
pages/_workspace_id/external/upload.vue- External upload page- Guest upload interface
- OTP input
- External upload queue
pages/_workspace_id/external/verify.vue- External verification page- OTP verification interface
- Access request handling
- Verification status
pages/_workspace_id/external/request-access.vue- External request access page- Access request form
- Email/contact input
- Request submission
pages/_workspace_id/external/request-submitted.vue- External request submitted page- Confirmation page
- Next steps information
- Status display
Additional Files ​
components/dam/AssetList/Versions.vue- May handle version uploads- Version upload interface
- Version management
- Version history
Admin Upload System ​
Upload Common Mixin ​
File: mixins/upload-common.js
This mixin provides common upload functionality shared across upload components.
Key Methods ​
javascript
// File validation
validateFile(file) {
// Check file size
// Check file type
// Return validation result
}
// Initialize upload
initUpload(file, options) {
// Prepare file for upload
// Set upload metadata
// Return upload object
}
// Track progress
updateProgress(uploadId, progress) {
// Update upload progress
// Emit progress events
}
// Handle errors
handleUploadError(uploadId, error) {
// Log error
// Update upload status
// Trigger retry if applicable
}Usage ​
vue
<script>
import uploadCommon from '~/mixins/upload-common'
export default {
mixins: [uploadCommon],
methods: {
async handleFileSelect(files) {
for (const file of files) {
const validation = this.validateFile(file)
if (validation.valid) {
const upload = this.initUpload(file, {
workspaceId: this.$route.params.workspace_id,
folderId: this.currentFolderId
})
this.addToQueue(upload)
} else {
this.showError(validation.message)
}
}
}
}
}
</script>Upload Queue Mixin ​
File: mixins/uploadQueue.js
Manages the upload queue with concurrent upload limits, retry logic, and state management.
Key Methods ​
javascript
// Add file to queue
addToQueue(upload) {
// Add to queue array
// Trigger upload if under limit
}
// Remove from queue
removeFromQueue(uploadId) {
// Remove from queue
// Cancel active upload if in progress
}
// Pause upload
pauseUpload(uploadId) {
// Pause upload
// Save current state
}
// Resume upload
resumeUpload(uploadId) {
// Resume from saved state
// Continue upload
}
// Process queue
processQueue() {
// Check concurrent limit
// Start next uploads
// Manage queue state
}Queue State ​
javascript
data() {
return {
uploadQueue: [], // Array of upload objects
activeUploads: [], // Currently uploading files
maxConcurrent: 3, // Max concurrent uploads
pausedUploads: [], // Paused uploads
failedUploads: [] // Failed uploads (for retry)
}
}Usage ​
vue
<script>
import uploadQueue from '~/mixins/uploadQueue'
export default {
mixins: [uploadQueue],
methods: {
handleFiles(files) {
files.forEach(file => {
const upload = {
id: this.generateId(),
file: file,
status: 'pending',
progress: 0,
error: null
}
this.addToQueue(upload)
})
}
}
}
</script>Asset Upload Component ​
File: components/dam/AssetUpload.vue
Main upload component providing file selection, drag-and-drop, and upload queue display.
Features ​
- Drag and drop file selection
- Multiple file selection
- File validation
- Upload queue display
- Progress tracking
- Error handling
- Retry functionality
Props ​
javascript
{
workspaceId: {
type: String,
required: true
},
folderId: {
type: String,
default: null
},
maxFileSize: {
type: Number,
default: 1073741824 // 1GB
},
allowedTypes: {
type: Array,
default: () => []
},
maxConcurrent: {
type: Number,
default: 3
}
}Events ​
javascript
{
'upload-complete': (asset) => {}, // Upload completed
'upload-error': (error) => {}, // Upload error
'upload-progress': (progress) => {}, // Progress update
'queue-updated': (queue) => {} // Queue state changed
}Usage ​
vue
<template>
<AssetUpload
:workspace-id="workspaceId"
:folder-id="currentFolderId"
:max-file-size="maxFileSize"
:allowed-types="allowedFileTypes"
@upload-complete="handleUploadComplete"
@upload-error="handleUploadError"
/>
</template>Chunked Upload Component ​
File: components/dam/upload-chunk.vue
Handles chunked/multipart uploads for large files.
Chunking Logic ​
javascript
// Split file into chunks
createChunks(file, chunkSize = 5 * 1024 * 1024) {
const chunks = []
let offset = 0
while (offset < file.size) {
const chunk = file.slice(offset, offset + chunkSize)
chunks.push({
data: chunk,
partNumber: chunks.length + 1,
offset: offset,
size: chunk.size
})
offset += chunkSize
}
return chunks
}Upload Flow ​
javascript
async uploadChunked(file) {
// 1. Start multipart upload
const { uploadId, assetId } = await this.$axios.get('/s3/start-upload', {
params: {
workspaceId: this.workspaceId,
fileType: file.type
}
})
// 2. Create chunks
const chunks = this.createChunks(file)
const parts = []
// 3. Upload each chunk
for (const chunk of chunks) {
// Get presigned URL
const { url } = await this.$axios.get('/s3/get-upload-url', {
params: {
PartNumber: chunk.partNumber,
UploadId: uploadId,
assetId: assetId,
workspaceId: this.workspaceId
}
})
// Upload chunk to S3
const etag = await this.uploadChunkToS3(url, chunk.data)
parts.push({
ETag: etag,
PartNumber: chunk.partNumber
})
// Update progress
this.updateProgress((chunk.partNumber / chunks.length) * 100)
}
// 4. Complete upload
await this.$axios.post('/s3/complete-upload', {
uploadId,
assetId,
workspaceId: this.workspaceId,
parts
})
}Upload Pages ​
Upload Page ​
File: pages/_workspace_id/dam/upload.vue
Main upload interface page.
Route: /:workspace_id/dam/upload
Features:
- File selection interface
- Upload queue management
- Progress monitoring
- Upload history
- Error handling
Uploaded Assets Page ​
File: pages/_workspace_id/dam/uploaded.vue
Displays list of uploaded assets.
Route: /:workspace_id/dam/uploaded
Features:
- List of uploaded assets
- Upload status tracking
- Asset actions (view, download, delete)
- Filtering and sorting
External Upload System ​
External Uploads Mixin ​
File: mixins/external-uploads.js
Handles external/guest user uploads with OTP authentication.
Key Methods ​
javascript
// Verify OTP
async verifyOTP(otpCode) {
// Verify OTP with backend
// Store verification token
// Grant upload access
}
// External upload
async externalUpload(file, otpToken) {
// Validate guest access
// Initialize external upload
// Use external upload queue
}
// Request access
async requestAccess(email, message) {
// Submit access request
// Send notification
// Return request ID
}External Upload Middleware ​
External Upload Auth ​
File: middleware/external-upload-auth.js
Validates guest user authentication for external uploads.
javascript
export default function ({ route, store, redirect }) {
// Check for guest token
// Validate token
// Redirect if invalid
// Allow access if valid
}External Guest Redirect ​
File: middleware/external-guest-redirect.js
Handles redirects for guest users.
javascript
export default function ({ route, store, redirect }) {
// Check if guest user
// Redirect to appropriate page
// Handle access requests
}External OTP Verify ​
File: middleware/external-otp-verify.js
Verifies OTP codes for external uploads.
javascript
export default async function ({ route, store, redirect, $axios }) {
// Get OTP from route/query
// Verify OTP with backend
// Store verification status
// Redirect based on result
}External Upload Component ​
File: components/dam/upload-chunk-external.vue
Chunked upload component specifically for external/guest users.
Features:
- OTP-verified uploads
- Guest user file handling
- External upload queue
- Limited access controls
External Upload Pages ​
External Upload Page ​
File: pages/_workspace_id/external/upload.vue
Route: /:workspace_id/external/upload
Features:
- Guest upload interface
- OTP input/verification
- External upload queue
- Limited functionality
External Verify Page ​
File: pages/_workspace_id/external/verify.vue
Route: /:workspace_id/external/verify
Features:
- OTP verification interface
- Access request handling
- Verification status display
External Request Access Page ​
File: pages/_workspace_id/external/request-access.vue
Route: /:workspace_id/external/request-access
Features:
- Access request form
- Email/contact input
- Request submission
- Validation
External Request Submitted Page ​
File: pages/_workspace_id/external/request-submitted.vue
Route: /:workspace_id/external/request-submitted
Features:
- Confirmation message
- Next steps information
- Status display
Upload Queue System ​
Queue Management ​
The upload queue system manages multiple file uploads with the following features:
- Concurrent Upload Limits: Maximum number of simultaneous uploads
- Queue Processing: Automatic queue processing when slots available
- Priority Management: Priority-based queue ordering
- State Persistence: Queue state saved across page reloads
Queue States ​
javascript
{
pending: 'pending', // Waiting in queue
uploading: 'uploading', // Currently uploading
paused: 'paused', // Paused by user
completed: 'completed', // Upload completed
error: 'error', // Upload failed
cancelled: 'cancelled' // Cancelled by user
}Progress Tracking ​
javascript
{
uploadId: 'unique-id',
fileName: 'example.jpg',
fileSize: 5242880,
uploaded: 2621440,
percentage: 50,
speed: 1024000, // bytes per second
timeRemaining: 2.5, // seconds
status: 'uploading'
}Error Handling and Retry ​
javascript
// Automatic retry logic
async retryUpload(uploadId) {
const upload = this.getUpload(uploadId)
// Reset upload state
upload.status = 'pending'
upload.error = null
upload.retryCount = (upload.retryCount || 0) + 1
// Check retry limit
if (upload.retryCount <= this.maxRetries) {
// Re-add to queue
this.addToQueue(upload)
} else {
// Mark as failed
upload.status = 'error'
this.handleFinalFailure(upload)
}
}Chunked Upload Implementation ​
Chunk Creation ​
Files are split into chunks based on size threshold (typically 5MB per chunk).
javascript
const CHUNK_SIZE = 5 * 1024 * 1024 // 5MB
function createChunks(file) {
const chunks = []
let start = 0
while (start < file.size) {
const end = Math.min(start + CHUNK_SIZE, file.size)
chunks.push({
blob: file.slice(start, end),
partNumber: chunks.length + 1,
start: start,
end: end,
size: end - start
})
start = end
}
return chunks
}Multipart Upload Flow ​
1. Start Multipart Upload
↓
GET /s3/start-upload
Returns: { uploadId, assetId }
↓
2. For Each Chunk:
↓
a. Get Presigned URL
GET /s3/get-upload-url
Returns: { url, expiresIn }
↓
b. Upload Chunk to S3
PUT {url} (direct to S3)
Returns: ETag header
↓
c. Store Part Info
{ ETag, PartNumber }
↓
3. Complete Multipart Upload
↓
POST /s3/complete-upload
Body: { uploadId, assetId, parts: [{ETag, PartNumber}] }
↓
4. Asset Created
↓
Backend creates asset record
Generates thumbnail (async)
Indexes in Typesense (async)Progress Tracking Per Chunk ​
javascript
// Track progress for each chunk
updateChunkProgress(uploadId, chunkIndex, progress) {
const upload = this.getUpload(uploadId)
const totalChunks = upload.chunks.length
const chunkProgress = progress / totalChunks
const completedChunks = upload.completedChunks || 0
upload.progress = (completedChunks / totalChunks) * 100 + chunkProgress
this.emit('progress', {
uploadId,
progress: upload.progress,
chunkIndex,
chunkProgress
})
}Error Recovery ​
javascript
// Handle chunk upload failure
async handleChunkError(uploadId, chunkIndex, error) {
const upload = this.getUpload(uploadId)
const chunk = upload.chunks[chunkIndex]
// Retry chunk upload
if (chunk.retryCount < this.maxChunkRetries) {
chunk.retryCount++
await this.uploadChunk(uploadId, chunkIndex)
} else {
// Abort multipart upload
await this.abortMultipartUpload(upload.uploadId)
upload.status = 'error'
upload.error = error
}
}API Integration ​
S3 Upload API ​
File: api/s3.js
See S3 API Documentation for complete API reference.
Key Endpoints ​
GET /s3/start-upload- Initiate multipart uploadGET /s3/get-upload-url- Get presigned URL for chunkPOST /s3/complete-upload- Complete multipart uploadPOST /api/assets/upload- Direct upload endpoint
Upload Endpoints ​
Direct Upload ​
javascript
POST /api/assets/upload
Content-Type: multipart/form-data
FormData:
- file: File object
- workspace_id: string
- folder_id: string (optional)
- metadata: object (optional)Multipart Upload ​
See AWS S3 Integration for complete multipart upload flow.
Workflows ​
Admin Upload Workflow ​
1. User navigates to upload page
Route: /:workspace_id/dam/upload
Component: pages/_workspace_id/dam/upload.vue
↓
2. User selects file(s)
Component: AssetUpload.vue
- Drag and drop OR file input
- Multiple file selection supported
↓
3. File validation
Mixin: upload-common.js
- Check file size (maxFileSize limit)
- Check file type (allowedTypes)
- Validate permissions (workspace/folder)
- Show error if validation fails
↓
4. Initialize upload objects
Mixin: upload-common.js
- Create upload object for each file
- Set metadata (workspaceId, folderId, etc.)
- Generate unique upload ID
↓
5. Add to upload queue
Mixin: uploadQueue.js
- Add upload to queue array
- Set status: 'pending'
- Display in UploadedListItem component
↓
6. Queue processing
Mixin: uploadQueue.js
- Check concurrent upload limit (maxConcurrent)
- If slot available → Start upload
- If queue full → Wait for slot
↓
7. Upload decision
Component: upload-chunk.vue (for large files)
- If file < threshold (e.g., 10MB) → Direct upload
POST /api/assets/upload (FormData)
- If file >= threshold → Chunked upload
Use multipart upload flow
↓
8. Upload execution
For Direct Upload:
- POST /api/assets/upload
- Track progress via onUploadProgress
- Update progress in queue
↓
For Chunked Upload:
- Start multipart upload
- Upload chunks sequentially/parallel
- Track progress per chunk
- Handle chunk errors
↓
9. Error handling
- If error occurs → Retry logic
- Update status: 'error'
- Show error message
- Allow manual retry
↓
10. Upload completion
- Backend creates asset record
- Generate thumbnail (async background job)
- Index in Typesense (async background job)
- Return asset data to frontend
↓
11. Update UI
Component: UploadedListItem.vue
- Update status: 'completed'
- Remove from active queue
- Show success message/notification
- Emit 'upload-complete' event
↓
12. Refresh asset list
- Update DAM store
- Refresh asset list if on uploaded page
- Navigate to asset details (optional)Version Upload Workflow ​
1. User navigates to asset details
Route: /:workspace_id/dam/assets/:asset_id
↓
2. User clicks "Upload New Version"
Component: Versions.vue
Dialog: VersionUploadBackdrop.vue
↓
3. File selection
- Select new version file
- Validate file (same or compatible type)
↓
4. Version upload
- Use same upload flow as admin upload
- Associate with existing asset
- Version number auto-incremented
↓
5. Version creation
- Create version record
- Link to parent asset
- Store version metadata
↓
6. Update asset
- Set new version as current (optional)
- Update asset preview
- Show version historyExternal Upload Workflow ​
1. Guest user accesses external upload page
Route: /:workspace_id/external/upload
↓
2. Access verification
Middleware: external-upload-auth.js
- Check if access granted
- If no access → Redirect to request-access
↓
3. OTP verification (if required)
Route: /:workspace_id/external/verify
Middleware: external-otp-verify.js
- Enter OTP code
- Verify with backend
- Store verification token in session
↓
4. File selection
Component: upload-chunk-external.vue
- Select file(s)
- Validate file (size, type)
- Use external-uploads.js mixin
↓
5. Add to external upload queue
- Limited concurrent uploads (guest restrictions)
- Queue management via external-uploads mixin
↓
6. Upload execution
- Use external upload queue
- Chunked upload for large files
- Track progress per chunk
- Handle errors with retry
↓
7. Upload completion
- Create asset record
- Notify workspace owner (email/notification)
- Show confirmation message
- Redirect to success pageExternal Access Request Workflow ​
1. Guest user requests access
Route: /:workspace_id/external/request-access
↓
2. Fill access request form
- Enter email/contact information
- Optional message
- Submit request
↓
3. Backend processes request
- Store request in database
- Send notification to workspace owner
- Generate OTP (if auto-approve)
↓
4. Redirect to request-submitted page
Route: /:workspace_id/external/request-submitted
- Show confirmation message
- Display next steps
- Provide OTP (if applicable)
↓
5. Workspace owner approves (manual)
- Owner receives notification
- Owner approves/rejects request
- OTP sent to guest user
↓
6. Guest user verifies OTP
Route: /:workspace_id/external/verify
- Enter OTP code
- Verify and grant access
- Redirect to upload pageChunked Upload Workflow ​
1. File selected (large file >= threshold)
Component: upload-chunk.vue
Mixin: upload-common.js
↓
2. Start multipart upload
API: GET /s3/start-upload
Params: { workspaceId, fileType }
Response: { uploadId, assetId }
Store: uploadId and assetId in upload object
↓
3. Create chunks
Component: upload-chunk.vue
- Calculate chunk size (default: 5MB)
- Split file using File.slice()
- Create chunk objects:
{
blob: File slice,
partNumber: 1, 2, 3...,
start: byte offset,
end: byte offset,
size: chunk size
}
- Store chunks array in upload object
↓
4. For each chunk (sequential or parallel):
↓
a. Get presigned URL
API: GET /s3/get-upload-url
Params: {
PartNumber: chunk.partNumber,
UploadId: uploadId,
assetId: assetId,
workspaceId: workspaceId
}
Response: { url, expiresIn }
↓
b. Upload chunk to S3
Method: PUT (direct to S3, not through backend)
URL: presigned URL from step a
Body: chunk blob
Headers: Content-Type from file
Response: ETag in response header
↓
c. Store part info
parts.push({
ETag: etag,
PartNumber: chunk.partNumber
})
Update: completedChunks count
↓
d. Update progress
Calculate: (completedChunks / totalChunks) * 100
Emit: progress event
Update: UploadedListItem component
↓
5. Handle chunk errors
- If chunk upload fails:
- Retry chunk (up to maxChunkRetries)
- If retries exhausted → Abort multipart upload
- Update status: 'error'
↓
6. Complete multipart upload
API: POST /s3/complete-upload
Body: {
uploadId: uploadId,
assetId: assetId,
workspaceId: workspaceId,
parts: [
{ ETag: '...', PartNumber: 1 },
{ ETag: '...', PartNumber: 2 },
...
]
}
Response: { success: true, location, key }
↓
7. Backend processing
- Merge all parts in S3
- Create asset record in database
- Store S3 key and metadata
- Trigger background jobs:
* Generate thumbnail (async)
* Index in Typesense (async)
* Extract metadata (async)
↓
8. Upload completion
- Backend returns asset data
- Frontend updates status: 'completed'
- Remove from upload queue
- Emit 'upload-complete' event
- Show success notification
↓
9. UI update
- Refresh asset list
- Navigate to asset details (optional)
- Update DAM storeComponent Integration ​
Using Upload Components ​
vue
<template>
<div>
<!-- Main upload component -->
<AssetUpload
:workspace-id="workspaceId"
:folder-id="currentFolderId"
@upload-complete="handleUploadComplete"
/>
<!-- Upload queue display -->
<UploadedListItem
v-for="upload in uploadQueue"
:key="upload.id"
:upload="upload"
@retry="retryUpload"
@cancel="cancelUpload"
/>
</div>
</template>
<script>
import AssetUpload from '~/components/dam/AssetUpload.vue'
import UploadedListItem from '~/components/dam/Uploaded/UploadedListItem.vue'
import uploadQueue from '~/mixins/uploadQueue'
export default {
components: {
AssetUpload,
UploadedListItem
},
mixins: [uploadQueue],
data() {
return {
workspaceId: this.$route.params.workspace_id,
currentFolderId: null
}
},
methods: {
handleUploadComplete(asset) {
// Handle completed upload
this.$store.dispatch('dam/addAsset', asset)
this.$router.push(`/${this.workspaceId}/dam/assets/${asset.id}`)
}
}
}
</script>Related Documentation ​
- AWS S3 Integration - Storage backend and multipart uploads
- S3 API - S3 upload endpoints
- Mixins - Upload Common - Upload common mixin
- Mixins - Upload Queue - Upload queue mixin
- Mixins - External Uploads - External uploads mixin