This endpoint requires authentication. Users must be logged in to upload media files.
Overview
Upload media files to your personal Media Vault. Files are stored in Supabase Storage with automatic organization into folders based on the source parameter.
Request
- Content-Type:
multipart/form-data
- Cookie: Session authentication cookie
The media file to upload. Supported formats:
- Images: JPEG, PNG, GIF, WebP
- Videos: MP4, WebM
Size Limit: Maximum 100MB per file (configurable via app settings)
The source or category for organizing the uploaded file. Determines the folder structure in storage.Options:
general - General uploads folder
profile - Profile images folder
thumbnail - Thumbnail images folder
smart-caption - Smart Captions folder
babecock - Babecock Studio folder
hypnococks - HypnoCocks folder
File Upload Requirements
Allowed MIME Types
[
"image/jpeg",
"image/png",
"image/gif",
"image/webp",
"video/mp4",
"video/webm"
]
Allowed File Extensions
[".jpg", ".jpeg", ".png", ".gif", ".webp", ".mp4", ".webm"]
Security Validations
- Path traversal characters (
.., /, \\) are not allowed in filenames
- Special characters in filenames are sanitized and replaced with underscores
- Filenames are prefixed with timestamp and source to prevent conflicts
Storage Integration
Supabase Storage Structure
Files are organized in the user-media bucket with the following structure:
users/{userId}/{FolderName}/{source}-{timestamp}-{sanitizedFilename}
Folder Mapping:
source: "general" → General/
source: "profile" → Profile/
source: "thumbnail" → Thumbnails/
- Other sources →
{Source}/
Example File Path
users/user_123/General/general-1709856234567-my_image.jpg
Response
The public URL of the uploaded file in Supabase Storage
The unique filename generated for the uploaded file (includes timestamp and sanitization)
The full storage path of the file in the format: users/{userId}/{FolderName}/{fileName}
The source parameter that was provided in the request
Success Response Example
{
"fileUrl": "https://example.supabase.co/storage/v1/object/public/user-media/users/user_123/General/general-1709856234567-vacation_photo.jpg",
"fileName": "general-1709856234567-vacation_photo.jpg",
"filePath": "users/user_123/General/general-1709856234567-vacation_photo.jpg",
"source": "general"
}
Error Responses
400 Bad Request
No file uploaded:
{
"message": "No file uploaded"
}
File too large:
{
"message": "File is too large to upload.",
"error": "FILE_TOO_LARGE",
"suggestion": "Please use a smaller file or check the upload size limit in settings. Consider compressing your image before uploading."
}
Invalid file type:
{
"message": "Upload failed: Invalid MIME type: application/pdf. Only JPEG, PNG, GIF, WebP images, and MP4/WebM videos are allowed.",
"error": "UPLOAD_ERROR",
"suggestion": "Please try again with a different file or contact support if the issue persists."
}
500 Internal Server Error
Storage error:
{
"message": "Failed to upload to cloud storage",
"error": "STORAGE_ERROR",
"suggestion": "Please try again. If the issue persists, contact support."
}
503 Service Unavailable
Storage not configured:
{
"message": "File storage is not configured. Please contact support.",
"error": "STORAGE_CONFIG_ERROR",
"suggestion": "File uploads are temporarily unavailable. Please try again later or contact support."
}
Example Usage
cURL
curl -X POST https://your-domain.com/api/media/upload \
-H "Cookie: connect.sid=your-session-cookie" \
-F "file=@/path/to/image.jpg" \
-F "source=general"
JavaScript (Fetch API)
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('source', 'general');
const response = await fetch('/api/media/upload', {
method: 'POST',
body: formData,
credentials: 'include'
});
const result = await response.json();
console.log('Uploaded file:', result.fileUrl);
React Example with Preview
import { useState } from 'react';
function MediaUploader() {
const [uploading, setUploading] = useState(false);
const [uploadedUrl, setUploadedUrl] = useState(null);
const handleUpload = async (e) => {
const file = e.target.files[0];
if (!file) return;
setUploading(true);
const formData = new FormData();
formData.append('file', file);
formData.append('source', 'general');
try {
const response = await fetch('/api/media/upload', {
method: 'POST',
body: formData,
credentials: 'include'
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
const result = await response.json();
setUploadedUrl(result.fileUrl);
} catch (error) {
console.error('Upload failed:', error);
alert(error.message);
} finally {
setUploading(false);
}
};
return (
<div>
<input
type="file"
onChange={handleUpload}
accept="image/*,video/*"
disabled={uploading}
/>
{uploading && <p>Uploading...</p>}
{uploadedUrl && (
<div>
<p>Upload successful!</p>
<img src={uploadedUrl} alt="Uploaded" style={{ maxWidth: '300px' }} />
</div>
)}
</div>
);
}
Notes
- All uploads are tracked in user activity logs with action
media_uploaded
- The maximum upload size can be configured by admins via app settings
- Files are automatically validated for MIME type and file extension
- Duplicate filenames are prevented by adding timestamps
- Uploaded files can be accessed immediately via the returned public URL