Copied to clipboard!

Chapter 5: File Management

Vibe CMS provides a robust file management system designed for modern web applications. This chapter covers everything from uploading files to organizing assets, optimizing images, and serving them efficiently through CDN-friendly URLs.

Overview

The file management system in Vibe CMS includes:

  • Multiple upload methods: Direct upload, URL download, and token-based uploads
  • Intelligent deduplication: SHA-256 hashing prevents duplicate storage
  • Folder organization: Hierarchical folder structure for logical asset organization
  • Rich metadata: SEO fields, descriptions, and technical metadata
  • Automatic image optimization: Multiple variants generated automatically
  • CDN-ready serving: Public asset URLs optimized for global delivery

File Upload System

Upload Methods

Vibe CMS supports three primary upload methods:

1. URL-Based Upload

Download a file from a URL and upload it to your CMS:

result = mcp.upload_file_from_url(
    url="https://images.unsplash.com/photo-1234567890",
    filename="hero-image.jpg",
    folder_path="/images/heroes",
    title="Homepage Hero Image",
    alt_text="Modern office workspace with natural lighting",
    focus_keyword="workspace design",
    description="Hero image for homepage featuring collaborative workspace"
)

file_id = result["file_id"]
public_url = result["public_url"]

Allowed domains for URL downloads include:

  • example.com
  • images.unsplash.com
  • picsum.photos
  • And other configured domains

2. Token-Based Upload (Direct Client Upload)

For direct uploads from client applications without exposing API credentials:

Step 1: Request an upload token

token_response = mcp.request_upload_token(
    filename="presentation.pdf",
    mime_type="application/pdf",
    file_size=2048576,  # 2MB in bytes
    folder_path="/documents/2024",
    title="Q1 Product Presentation",
    alt_text="Q1 2024 product roadmap presentation"
)

upload_url = token_response["upload_url"]
file_id = token_response["file_id"]

Step 2: Upload file directly via HTTP PUT

// Client-side JavaScript example
const file = document.querySelector('input[type="file"]').files[0];

const response = await fetch(uploadUrl, {
    method: 'PUT',
    body: file,
    headers: {
        'Content-Type': file.type
    }
});

if (response.ok) {
    console.log('Upload successful! File ID:', fileId);
}

This method is ideal for:

  • Browser-based uploads
  • Mobile applications
  • Serverless architectures
  • Scenarios where you want to avoid proxying large files through your backend

3. Local File Upload (SDK)

When using the TypeScript or Python SDK directly:

import { VibeCMS } from '@vibe-cms/sdk';

const cms = new VibeCMS({
    apiUrl: process.env.VIBE_API_URL,
    projectId: process.env.VIBE_PROJECT_ID,
    apiKey: process.env.VIBE_API_KEY
});

const result = await cms.uploadFile({
    file: fileBuffer,
    filename: 'logo.png',
    mimeType: 'image/png',
    folderPath: '/images/branding',
    title: 'Company Logo',
    altText: 'Acme Corporation Logo'
});

File Size Limits

  • Maximum file size: 52,428,800 bytes (50 MB)
  • Files exceeding this limit will be rejected
  • Consider compression for large images or videos

File Deduplication

Vibe CMS uses SHA-256 hashing to prevent duplicate file storage:

  1. When a file is uploaded, its SHA-256 hash is calculated
  2. If a file with the same hash already exists, the system:
    • Creates a new file record with metadata
    • Links to the existing physical file
    • Saves storage space automatically
  3. Each file record can have unique metadata even if sharing the same physical file

This means:

  • ✅ Same image can have different titles, alt text, or descriptions
  • ✅ Storage costs are minimized
  • ✅ Upload speed is faster for duplicate files
  • ✅ No manual deduplication needed

Supported MIME Types

Vibe CMS supports all standard MIME types including:

Images:

  • image/jpeg, image/jpg
  • image/png
  • image/gif
  • image/webp
  • image/svg+xml

Documents:

  • application/pdf
  • application/msword
  • application/vnd.openxmlformats-officedocument.wordprocessingml.document
  • text/plain

Media:

  • video/mp4
  • audio/mpeg
  • audio/wav

Archives:

  • application/zip
  • application/x-tar

And many more standard types.

File Organization

Folder Structure

Folders in Vibe CMS use a path-based hierarchy similar to filesystem directories:

/
├── images/
│   ├── heroes/
│   ├── products/
│   └── team/
├── documents/
│   ├── 2024/
│   │   ├── contracts/
│   │   └── reports/
│   └── 2023/
└── media/
    ├── videos/
    └── audio/

Creating Folders

# Create a top-level folder
result = mcp.manage_folder(
    action="create",
    path="/images"
)

# Create nested folders
result = mcp.manage_folder(
    action="create",
    path="/documents/2024/contracts"
)

Important: Parent folders must exist before creating nested folders.

Listing Folders

# List all folders
all_folders = mcp.folders()

# List folders in a specific parent path
image_folders = mcp.folders(parent_path="/images")
doc_folders = mcp.folders(parent_path="/documents/2024")

Response format:

{
    "folders": [
        {"path": "/images", "created_at": "2024-01-15T10:30:00Z"},
        {"path": "/images/heroes", "created_at": "2024-01-15T10:31:00Z"}
    ],
    "count": 2,
    "filter_applied": {
        "parent_path": "/images"
    }
}

Renaming Folders

result = mcp.manage_folder(
    action="update",
    path="/images/heros",  # Old path (typo)
    new_path="/images/heroes"  # Corrected path
)

Note: Renaming a folder updates all file paths within it automatically.

Deleting Folders

# Requires confirmation to prevent accidental deletion
result = mcp.manage_folder(
    action="delete",
    path="/temp",
    confirm_delete=True
)

Warning: Deleting a folder does NOT delete the files within it. Files will remain but their folder path will be cleared.

Moving Files Between Folders

Move a file by updating its folder_path metadata:

result = mcp.manage_file(
    action="update",
    file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479",
    folder_path="/images/archived"
)

File Metadata

Core Metadata Fields

Every file in Vibe CMS has comprehensive metadata:

{
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "filename": "hero-image.jpg",
    "mime_type": "image/jpeg",
    "file_size": 245760,  # bytes
    "folder_path": "/images/heroes",
    "sha256_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    
    # Descriptive metadata
    "title": "Homepage Hero Image",
    "description": "Modern office workspace featuring collaboration",
    "caption": "Our team collaborating on innovative solutions",
    
    # SEO metadata
    "alt_text": "Modern office workspace with natural lighting",
    "focus_keyword": "workspace design",
    
    # Image-specific metadata
    "width": 1920,
    "height": 1080,
    
    # System metadata
    "created_at": "2024-01-15T10:30:00Z",
    "updated_at": "2024-01-15T14:22:00Z",
    "public_url": "https://assets.vibecms.com/..."
}

Metadata Field Descriptions

Field Type Description Required
filename string Original filename Yes
mime_type string MIME type (e.g., image/jpeg) Yes
file_size integer Size in bytes Yes (auto)
folder_path string Folder location (e.g., /images) No
title string Display title for CMS No
description string Detailed description No
caption string Brief caption text No
alt_text string SEO alt text for images No
focus_keyword string Primary SEO keyword No
width integer Image width in pixels Auto
height integer Image height in pixels Auto
sha256_hash string SHA-256 hash for deduplication Yes (auto)

Updating File Metadata

result = mcp.manage_file(
    action="update",
    file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479",
    title="Updated Hero Image",
    alt_text="Team members collaborating in modern workspace",
    focus_keyword="team collaboration",
    description="Updated description with more detail",
    caption="Innovation through collaboration",
    folder_path="/images/heroes/2024"
)

Note: You can update any combination of fields in a single call. Only provided fields will be updated.

Image Optimization and Transformations

Automatic Image Optimization

When an image is uploaded, Vibe CMS automatically:

  1. Analyzes the image - Extracts width, height, and format
  2. Generates variants - Creates optimized versions for different use cases
  3. Optimizes file size - Compresses without significant quality loss
  4. Calculates hash - SHA-256 for deduplication

File Variants System

Vibe CMS generates multiple variants of each image:

# Variant configuration examples
variants = {
    "thumbnail": {
        "width": 150,
        "height": 150,
        "format": "webp"
    },
    "medium": {
        "width": 800,
        "height": 600,
        "format": "webp"
    },
    "large": {
        "width": 1920,
        "height": 1080,
        "format": "webp"
    }
}

Variant Storage

Variants are stored in the cms_file_variants table:

CREATE TABLE cms_file_variants (
    id UUID PRIMARY KEY,
    file_id UUID REFERENCES cms_files(id),
    variant_name VARCHAR(100),  -- e.g., 'thumbnail', 'medium', 'large'
    width INTEGER,
    height INTEGER,
    file_size INTEGER,
    storage_path TEXT,
    created_at TIMESTAMP
);

Accessing Variants

When retrieving file metadata, variants are included:

file_data = mcp.files(file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479")

print(file_data["variants"])
# [
#     {
#         "name": "thumbnail",
#         "width": 150,
#         "height": 150,
#         "url": "https://assets.vibecms.com/.../thumbnail.webp"
#     },
#     {
#         "name": "medium",
#         "width": 800,
#         "height": 600,
#         "url": "https://assets.vibecms.com/.../medium.webp"
#     }
# ]

Preview Generation

Generate a 512x512 preview for image files:

preview = mcp.get_file_preview(
    file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479"
)

# Returns base64-encoded image data or preview URL
preview_url = preview["preview_url"]

Use cases:

  • Admin interface thumbnails
  • Quick image previews
  • Gallery grids
  • File selection dialogs

On-Demand Transformations

For custom transformations beyond predefined variants, use URL parameters:

https://assets.vibecms.com/files/{file_id}?w=400&h=300&fit=cover&format=webp

Supported parameters:

  • w - Width in pixels
  • h - Height in pixels
  • fit - Resize mode: cover, contain, fill, inside, outside
  • format - Output format: webp, jpeg, png, avif
  • quality - Quality: 1-100

Asset Serving

Public Asset URLs

Every file has a public URL for serving:

file_data = mcp.files(file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479")
public_url = file_data["public_url"]

# Example URL:
# https://assets.vibecms.com/projects/proj_abc123/files/f47ac10b-58cc-4372-a567-0e02b2c3d479/hero-image.jpg

CDN-Friendly URLs

Asset URLs are optimized for CDN caching:

  • Consistent URLs - Same file always has the same URL
  • Cache headers - Long cache times with proper invalidation
  • Global distribution - Served from edge locations worldwide
  • HTTPS by default - Secure asset delivery

SDK Asset URL Generation

The TypeScript SDK provides helper methods:

import { cms } from './lib/cms';

// Get file metadata including public URL
const file = await cms.getFile('f47ac10b-58cc-4372-a567-0e02b2c3d479');
const url = file.publicUrl;

// Get specific variant URL
const thumbnailUrl = file.variants.find(v => v.name === 'thumbnail')?.url;

Serving Optimized Images

Best practices for serving images:

<!-- Use appropriate variant for context -->
<img 
    src="{medium_variant_url}"
    srcset="
        {thumbnail_url} 150w,
        {medium_url} 800w,
        {large_url} 1920w
    "
    sizes="(max-width: 600px) 150px, (max-width: 1200px) 800px, 1920px"
    alt="{alt_text}"
    loading="lazy"
/>

Downloading Assets Programmatically

import requests

file_data = mcp.files(file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479")
public_url = file_data["public_url"]

# Download the file
response = requests.get(public_url)
with open('downloaded_file.jpg', 'wb') as f:
    f.write(response.content)

File Management Operations

Listing Files

List all files:

all_files = mcp.files()

print(f"Total files: {all_files['count']}")
for file in all_files['files']:
    print(f"{file['filename']} - {file['mime_type']}")

Filter by folder:

image_files = mcp.files(folder_path="/images")
contract_files = mcp.files(folder_path="/documents/2024/contracts")

Filter by MIME type:

# All images
images = mcp.files(mime_type="image/")

# Specific image type
jpegs = mcp.files(mime_type="image/jpeg")

# PDFs only
pdfs = mcp.files(mime_type="application/pdf")

Combine filters:

product_images = mcp.files(
    folder_path="/images/products",
    mime_type="image/"
)

Getting File Metadata

# Get specific file by ID
file_data = mcp.files(file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479")

print(f"Filename: {file_data['filename']}")
print(f"Size: {file_data['file_size']} bytes")
print(f"Public URL: {file_data['public_url']}")
print(f"Variants: {len(file_data['variants'])}")

Updating Files

Update metadata without changing the file itself:

result = mcp.manage_file(
    action="update",
    file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479",
    title="Q1 2024 Product Launch Hero",
    alt_text="Innovative product showcase with modern design",
    focus_keyword="product innovation",
    description="Hero image for Q1 product launch campaign",
    folder_path="/images/campaigns/2024-q1"
)

Deleting Files

# Permanent deletion - requires confirmation
result = mcp.manage_file(
    action="delete",
    file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479",
    confirm_delete=True
)

Warning:

  • File deletion is permanent and cannot be undone
  • All variants are also deleted
  • Content items referencing this file will have broken references
  • The physical file is deleted only if no other file records reference it (due to deduplication)

File Search and Filtering

Advanced search patterns:

# Find all team photos from 2024
team_photos = mcp.files(
    folder_path="/images/team",
    mime_type="image/"
)

# Filter results by filename pattern
import re
headshots = [
    f for f in team_photos['files'] 
    if re.search(r'headshot', f['filename'], re.IGNORECASE)
]

# Find large files (>5MB)
large_files = [
    f for f in all_files['files']
    if f['file_size'] > 5 * 1024 * 1024
]

MCP Tools for File Management

files() - List and Get File Metadata

# List all files
mcp.files()

# Get specific file
mcp.files(file_id="f47ac10b-...")

# Filter by folder
mcp.files(folder_path="/images")

# Filter by MIME type
mcp.files(mime_type="image/png")

# Combine filters
mcp.files(folder_path="/images/products", mime_type="image/")

Returns:

  • Single file: {id, filename, mime_type, file_size, public_url, variants, ...}
  • Multiple files: {files: [...], count: N, filter_applied: {...}}

manage_file() - Update or Delete Files

# Update metadata
mcp.manage_file(
    action="update",
    file_id="f47ac10b-...",
    title="New Title",
    alt_text="Updated alt text",
    focus_keyword="keyword",
    description="Updated description",
    caption="Updated caption",
    folder_path="/new/folder"
)

# Delete file
mcp.manage_file(
    action="delete",
    file_id="f47ac10b-...",
    confirm_delete=True
)

get_file_preview() - Generate Image Previews

# Get 512x512 preview
preview = mcp.get_file_preview(
    file_id="f47ac10b-58cc-4372-a567-0e02b2c3d479"
)

preview_url = preview["preview_url"]

Only works for image files - Returns error for non-image types.

upload_file_from_url() - Download and Upload from URL

result = mcp.upload_file_from_url(
    url="https://images.unsplash.com/photo-123...",
    filename="hero.jpg",  # Optional, auto-detected if omitted
    mime_type="image/jpeg",  # Optional, auto-detected if omitted
    folder_path="/images/heroes",
    title="Homepage Hero",
    alt_text="Modern workspace design",
    focus_keyword="workspace",
    description="Hero image for homepage",
    caption="Innovation in action"
)

file_id = result["file_id"]
public_url = result["public_url"]

request_upload_token() - Get Token for Direct Upload

token = mcp.request_upload_token(
    filename="document.pdf",
    mime_type="application/pdf",
    file_size=2048576,  # Required: size in bytes
    folder_path="/documents",
    title="Contract Document",
    alt_text="Q1 2024 Service Contract"
)

upload_url = token["upload_url"]  # Use for HTTP PUT
file_id = token["file_id"]  # File ID for later reference

Maximum file size: 52,428,800 bytes (50 MB)

folders() - List Folders

# List all folders
mcp.folders()

# List folders in specific parent
mcp.folders(parent_path="/images")
mcp.folders(parent_path="/documents/2024")

Returns: {folders: [...], count: N, filter_applied: {...}}

manage_folder() - Create, Rename, Delete Folders

# Create folder
mcp.manage_folder(
    action="create",
    path="/images/products"
)

# Rename folder
mcp.manage_folder(
    action="update",
    path="/images/prodcuts",  # Old path
    new_path="/images/products"  # New path
)

# Delete folder
mcp.manage_folder(
    action="delete",
    path="/temp",
    confirm_delete=True
)

Best Practices

File Naming Conventions

DO:

  • ✅ Use descriptive, lowercase names: team-photo-john-smith.jpg
  • ✅ Use hyphens instead of spaces: product-hero-2024.png
  • ✅ Include context in filename: blog-header-ai-trends.jpg
  • ✅ Use consistent date format: report-2024-03-15.pdf
  • ✅ Keep names concise but meaningful

DON'T:

  • ❌ Avoid spaces: team photo.jpgteam-photo.jpg
  • ❌ Avoid special characters: image@#$%.jpg
  • ❌ Don't use generic names: image1.jpg, photo.png
  • ❌ Avoid very long names (>100 characters)

Folder Organization Strategies

By Content Type:

/images/
/videos/
/documents/
/downloads/

By Date:

/images/2024/01/
/images/2024/02/
/documents/2024/q1/

By Purpose:

/marketing/campaigns/2024-q1/
/marketing/social-media/
/product/screenshots/
/team/headshots/

Hybrid Approach (Recommended):

/images/
  ├── marketing/
  │   ├── campaigns/2024-q1/
  │   └── social/
  ├── products/
  │   ├── screenshots/
  │   └── packaging/
  └── team/
/documents/
  ├── 2024/
  │   ├── contracts/
  │   └── reports/
  └── templates/

SEO Metadata Optimization

Alt Text Best Practices:

# Good alt text
alt_text = "Woman presenting data analytics on laptop to team in modern office"

# Bad alt text
alt_text = "image"  # Too generic
alt_text = "photo-123.jpg"  # Just filename
alt_text = ""  # Empty (never do this for content images)

Focus Keyword Strategy:

# Target 1-3 keywords that describe the image
focus_keyword = "data analytics presentation"

# Use in alt text naturally
alt_text = "Team reviewing data analytics presentation on laptop"

Title and Description:

title = "Q1 Analytics Review Meeting"  # Brief, descriptive
description = "Marketing team reviewing Q1 performance metrics and analytics data to plan Q2 strategy"  # Detailed context
caption = "Strategic planning in action"  # Short, engaging

Image Optimization Guidelines

Before Upload:

  1. Resize appropriately - Don't upload 4K images for thumbnails
  2. Compress images - Use tools like ImageOptim, TinyPNG
  3. Choose right format:
    • Photos: JPEG or WebP
    • Graphics/logos: PNG or SVG
    • Animations: GIF or WebP

After Upload:

  1. Use appropriate variants - Thumbnail for grids, medium for content, large for hero images
  2. Leverage lazy loading - Don't load images until needed
  3. Implement responsive images - Use srcset and sizes

Target sizes:

  • Thumbnails: 150-300px wide
  • Content images: 600-1200px wide
  • Hero images: 1920px wide maximum
  • File size: < 200KB for most images

Security Considerations

File Upload Security:

  • ✅ Validate MIME types server-side
  • ✅ Scan uploaded files for malware
  • ✅ Limit file sizes (50 MB maximum)
  • ✅ Use upload tokens for client-side uploads
  • ✅ Never expose API keys in client code

Access Control:

  • ✅ Public URLs are publicly accessible (by design)
  • ✅ Use private storage for sensitive documents
  • ✅ Don't upload confidential information to public CMS
  • ✅ Review folder permissions regularly

File Deletion:

  • ✅ Always require confirmation for deletion
  • ✅ Audit file deletions
  • ✅ Consider soft-delete for recovery options
  • ✅ Check content references before deleting

Performance Tips

Optimize Asset Delivery:

<!-- Use WebP with fallback -->
<picture>
    <source srcset="{webp_url}" type="image/webp">
    <img src="{jpeg_url}" alt="{alt_text}" loading="lazy">
</picture>

Batch Operations:

# Fetch all files once
all_files = mcp.files(folder_path="/images/products")

# Process in batches
for file in all_files['files']:
    # Update metadata
    mcp.manage_file(
        action="update",
        file_id=file['id'],
        focus_keyword="product showcase"
    )

Cache Metadata:

# Cache file metadata in your application
import functools
import time

@functools.lru_cache(maxsize=1000)
def get_cached_file(file_id, cache_time=3600):
    return mcp.files(file_id=file_id)

# Use cached version
file_data = get_cached_file("f47ac10b-...")

CDN Configuration:

  • Set long cache times for immutable assets
  • Use query parameters for cache busting: ?v=2024-03-15
  • Configure edge caching for global distribution
  • Enable compression (gzip, brotli) at CDN level

Summary

You've learned:

Upload Methods - URL download, token-based upload, direct SDK upload
Deduplication - SHA-256 hashing prevents duplicate storage
Organization - Hierarchical folder structure with path-based navigation
Metadata - Rich SEO and descriptive metadata for all files
Image Optimization - Automatic variant generation and on-demand transformations
Asset Serving - CDN-friendly public URLs with global delivery
MCP Tools - Complete file and folder management via MCP interface
Best Practices - Naming conventions, SEO optimization, security, performance

Next Steps

Additional Resources