Skip to main content
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

Headers

  • Content-Type: multipart/form-data
  • Cookie: Session authentication cookie

Form Data Parameters

file
file
required
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)
source
string
default:"general"
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

fileUrl
string
required
The public URL of the uploaded file in Supabase Storage
fileName
string
required
The unique filename generated for the uploaded file (includes timestamp and sanitization)
filePath
string
required
The full storage path of the file in the format: users/{userId}/{FolderName}/{fileName}
source
string
required
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

Build docs developers (and LLMs) love