Make A Media Manager With Cloudflare R2
Build a Client-Side Cloudflare R2 Media Manager with JavaScript File Uploads
Master Cloudflare R2 storage by building a browser-based media manager with vanilla JavaScript and the AWS SDK. This tutorial teaches S3-compatible API integration, client-side uploads, image optimization, and file management—all without a backend server. Perfect for managing static site assets at a fraction of S3 costs.
What You'll Build
- Client-side media manager with drag-and-drop uploads
- File browser with thumbnail previews and metadata
- Image optimization and automatic resizing
- File deletion and bulk operations
- Shareable public URLs for uploaded files
- Local-only tool with secure credential management
Cloudflare R2 and AWS SDK Skills You'll Learn
- R2 Setup: Create buckets, configure CORS, and generate API tokens
- AWS SDK Integration: Use S3Client for R2 operations in the browser
- File Uploads: PutObjectCommand with progress tracking
- File Listing: ListObjectsV2Command for browsing files
- Image Processing: Client-side optimization with Canvas API
- Drag and Drop: HTML5 File API for intuitive uploads
- Security: CORS policies and credential best practices
Prerequisites
- Cloudflare account (free tier available)
- Basic JavaScript knowledge (async/await, promises)
- Understanding of HTML and CSS fundamentals
- Familiarity with npm and module imports
- Code editor and local development server
Time Commitment: 3-4 hours. Build a practical tool for managing media files on Cloudflare R2.
Why Cloudflare R2 for Media Storage?
Cloudflare R2 offers S3-compatible storage with zero egress fees, making it dramatically cheaper than AWS S3 for public content. At $0.015/GB per month (with 10GB free), R2 can save you up to 90% compared to S3's egress charges. Combined with Cloudflare's global CDN and 300+ edge locations, R2 delivers fast, reliable media hosting worldwide.
This tutorial teaches you to build practical tooling for R2, perfect for managing images, assets, and media for static sites, blogs, or portfolios. The S3-compatible API means you can migrate from S3 with minimal changes.
Make A Media Manager With Cloudflare R2
Build a client-side media manager for Cloudflare R2 using pure JavaScript. No backend needed—upload, browse, preview, and delete files directly from your browser using the S3-compatible API. Perfect for managing images and assets for static sites, with drag-and-drop uploads, image optimization, metadata tagging, and shareable URLs. Keep your media organized in blazing-fast R2 storage at a fraction of S3 costs.
Web Development Web Setup: Installation and Project Configuration
Create bucket and get API credentials
Set up Cloudflare R2 bucket and API credentials. Log into Cloudflare dashboard and navigate to R2 Object Storage. Create a new R2 bucket with a descriptive name like "my-media-files". Configure bucket settings: keep it private initially (we'll configure public access for specific files later if needed). Generate R2 API token: - Go to R2 → Manage R2 API Tokens - Click "Create API Token" - Give it a name like "Media Manager Token" - Set permissions to "Object Read & Write" for your specific bucket - Copy the Access Key ID, Secret Access Key, and endpoint URL - Store these securely - you'll need them for the application Note the bucket endpoint format: https://<account-id>.r2.cloudflarestorage.com Document your credentials: - Account ID - Access Key ID - Secret Access Key - Bucket name - Endpoint URL Keep these credentials secure and never commit them to version control.
Enable browser access to R2
Configure CORS policy for R2 bucket to allow browser requests.
In Cloudflare R2 dashboard, select your bucket and go to Settings.
Find the CORS policy section and add a new CORS rule:
For local development, use permissive settings:
- Allowed Origins: http://localhost:* or http://127.0.0.1:* (or use * for testing)
- Allowed Methods: GET, PUT, POST, DELETE, HEAD
- Allowed Headers: * (or specifically: content-type, authorization, x-amz-*)
- Expose Headers: ETag, x-amz-request-id
- Max Age: 3600 seconds
Example CORS JSON configuration:
[
{
"AllowedOrigins": ["http://localhost:*", "http://127.0.0.1:*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3600
}
]
Apply the CORS policy to your bucket.
Important security note: For production, restrict AllowedOrigins to your specific domain instead of using wildcards.
Test CORS configuration is working by making a simple OPTIONS request from your browser console.Create HTML/CSS/JS structure
Create a frontend-only project structure for the R2 media manager.
Create project directory named "r2-media-manager" with the following structure:
r2-media-manager/
├── index.html (main HTML file)
├── styles.css (styling)
├── app.js (main application logic)
├── r2-client.js (R2 API wrapper)
└── config.js (configuration - gitignored)
Create index.html with:
- HTML5 boilerplate
- Links to styles.css
- Container for file upload area (drag & drop zone)
- Grid/list area to display uploaded files
- Script tags for AWS SDK from CDN
- Script tags for your app.js and r2-client.js
Create styles.css with:
- Modern, clean styling
- Responsive grid layout for file thumbnails
- Upload drop zone styling with hover effects
- Button and modal styles
- Mobile-friendly design
Create config.js template:
export const R2_CONFIG = {
accountId: 'YOUR_ACCOUNT_ID',
accessKeyId: 'YOUR_ACCESS_KEY_ID',
secretAccessKey: 'YOUR_SECRET_ACCESS_KEY',
bucketName: 'YOUR_BUCKET_NAME',
endpoint: 'https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com'
}
Add config.js to .gitignore to keep credentials secure.
Create a config.example.js showing the structure without real credentials.Database and Environment Configuration for Web Development
Initialize S3 client for R2
Initialize AWS SDK S3 client configured for Cloudflare R2.
In r2-client.js, import AWS SDK (or use from CDN):
- Use AWS SDK v3 (modular) or v2 (monolithic) from CDN
- For CDN: <script src="https://sdk.amazonaws.com/js/aws-sdk-2.1.x.min.js"></script>
Create R2Client class that initializes AWS S3 client:
Import configuration from config.js with credentials.
Configure AWS.S3 or S3Client with:
- endpoint: Your R2 endpoint URL
- region: 'auto' (R2 uses auto region)
- credentials: {
accessKeyId: from config
secretAccessKey: from config
}
- signatureVersion: 'v4'
- s3ForcePathStyle: true (important for R2)
Create initialization function that validates credentials.
Add error handling for invalid credentials or network issues.
Test connection by attempting to list bucket contents (can be empty initially).
Export the configured S3 client for use in app.js.
Handle CORS errors gracefully with user-friendly messages.Building Web Features: Core Functionality and Admin Panel
Implement drag-drop and click upload
Implement file upload functionality with drag-and-drop support.
In app.js, create upload handler:
Set up file input element with accept attribute for images (image/*) or all files (*).
Add drag-and-drop event listeners on drop zone:
- dragover: prevent default and add visual feedback
- dragleave: remove visual feedback
- drop: prevent default, get files from event.dataTransfer.files
On file selection (drop or click), read the files:
- Iterate through FileList
- Validate file type and size (e.g., max 10MB)
- Show preview for images using FileReader
- Display upload progress indicator
Upload files to R2 using S3 client:
- Use s3.putObject() or s3.upload() method
- Parameters: {
Bucket: bucketName,
Key: filename (consider adding timestamp or UUID to prevent collisions),
Body: file,
ContentType: file.type,
Metadata: optional metadata
}
Show upload progress:
- Use upload progress events if available
- Display progress bar or percentage
- Show success/error states
Handle upload completion:
- Add uploaded file to the displayed file list
- Clear the file input
- Show success notification
Implement error handling:
- Network errors
- Permission errors
- File size exceeded
- Invalid file types
Add batch upload support for multiple files.
Optional: Generate thumbnails for images before displaying in grid.Display all files from bucket
Implement file listing and display functionality.
Create listFiles() function using S3 client:
- Use s3.listObjectsV2() method
- Parameters: {
Bucket: bucketName,
MaxKeys: 1000 (adjust as needed)
}
- Handle pagination with ContinuationToken for buckets with many files
Parse response:
- Extract file metadata: Key, Size, LastModified, ETag
- Calculate human-readable file sizes (KB, MB, GB)
- Format dates nicely
Display files in the UI:
- Create grid or list layout
- For images: show thumbnail preview using R2 public URL or generate presigned URL
- For non-images: show file type icon
- Display filename, size, and date
- Add hover effects and selection states
Generate presigned URLs for file access:
- Use s3.getSignedUrl() or s3.getSignedUrlPromise()
- Parameters: {
Bucket: bucketName,
Key: filename,
Expires: 3600 (1 hour)
}
- Use these URLs to display/download files
Implement view switching:
- Toggle between grid and list views
- Save user preference in localStorage
Add search/filter functionality:
- Filter by filename
- Filter by file type (images, documents, etc.)
- Sort by date, name, or size
Add pagination or infinite scroll if handling many files.
Handle empty state when bucket is empty with friendly message.
Implement refresh button to reload file list.View images and download files
Implement file preview and download functionality.
Create preview modal/lightbox:
- Modal overlay that darkens background
- Large preview area for images
- File information sidebar (name, size, date, dimensions)
- Close button and keyboard navigation (ESC to close)
For image files:
- Display full-size image in modal
- Add zoom controls
- Add previous/next navigation for browsing through images
- Show loading spinner while image loads
For non-image files:
- Show file icon and metadata
- Provide download button
Generate presigned URLs for secure access:
- Use s3.getSignedUrl('getObject', params)
- Set expiration time (e.g., 1 hour)
- Use this URL as image src or download href
Implement download functionality:
- Create download button for each file
- Generate presigned download URL
- Add Content-Disposition header for proper filename
- Trigger browser download using anchor element or fetch + blob
Add click handlers:
- Click on file thumbnail to open preview
- Click on filename to download
- Right-click options for copy URL
Handle different file types:
- Images: inline preview
- Videos: video player (if needed)
- Documents: download only
- Text files: could show in preview with syntax highlighting
Implement keyboard shortcuts:
- Arrow keys: navigate between files
- ESC: close preview
- Space: toggle info panel
Add copy-to-clipboard for file URLs.
Show error states if file fails to load.Remove files from R2 bucket
Implement file deletion functionality with safety confirmations.
Add delete button to each file in the list:
- Trash icon button on hover or always visible
- Style with warning colors (red/orange)
Implement delete confirmation:
- Show modal dialog asking "Are you sure?"
- Display filename being deleted
- Include "Cancel" and "Delete" buttons
- Make "Delete" button require explicit click (not default)
Delete single file:
- Use s3.deleteObject() method
- Parameters: {
Bucket: bucketName,
Key: filename
}
- Handle response and errors
Update UI after deletion:
- Remove file from displayed list immediately (optimistic update)
- Show success notification
- If delete fails, restore file in list and show error
Implement batch/multi-delete:
- Add checkbox selection to each file
- "Select All" option
- Delete button that works on all selected files
- Confirmation dialog shows count: "Delete 5 files?"
- Use s3.deleteObjects() for batch deletion (more efficient)
Add undo functionality (optional):
- Brief timeout before actual deletion
- Show "Undoing..." notification
- Keep deleted file data in memory temporarily
Handle errors gracefully:
- Permission denied
- File not found (already deleted)
- Network errors
Add loading states during deletion:
- Disable delete button while processing
- Show spinner or progress indicator
Security considerations:
- Ensure only authenticated users can delete (if adding auth)
- Log deletions (optional)
- Consider soft delete with trash/recovery (advanced)
Keyboard shortcut: Delete key to delete selected files.Add tags, descriptions, and custom fields
Implement metadata management for uploaded files.
Add metadata when uploading files:
- Use Metadata parameter in s3.putObject()
- Store custom fields: description, tags, alt-text, category
- Metadata keys must be lowercase and use hyphens
- Example: { 'x-amz-meta-description': 'My image', 'x-amz-meta-tags': 'blog,tutorial' }
Create metadata editor UI:
- Edit button on each file
- Modal form with fields:
* Description (textarea)
* Tags (comma-separated or tag picker)
* Alt text (for images)
* Custom categories
- Save and Cancel buttons
Update metadata for existing files:
- Use s3.copyObject() to copy file to itself with new metadata
- Parameters: {
Bucket: bucketName,
CopySource: bucketName + '/' + filename,
Key: filename,
Metadata: updatedMetadata,
MetadataDirective: 'REPLACE'
}
Retrieve metadata when listing files:
- Use s3.headObject() to get metadata without downloading file
- Display metadata in file preview/info panel
Implement tag-based filtering:
- Extract all unique tags from files
- Show tag cloud or tag list
- Click tag to filter files by that tag
- Support multiple tag selection
Add search by metadata:
- Search in descriptions
- Filter by tags
- Filter by custom fields
Store metadata locally for faster filtering:
- Cache metadata in localStorage
- Sync with R2 on refresh
- Handle conflicts if file updated externally
Add bulk metadata editing:
- Select multiple files
- Add tags to all selected files
- Update common fields
Provide metadata export:
- Export all metadata as JSON
- Useful for backup or migration
Handle special characters in metadata values.
Consider using custom metadata schema that works for your use case.Create shareable public URLs
Implement URL generation for files in R2 bucket.
Configure R2 public access (optional):
- In R2 dashboard, enable "Public Access" on bucket if you want permanent URLs
- Or keep private and use presigned URLs only
Generate presigned URLs for temporary access:
- Use s3.getSignedUrl('getObject', { Bucket, Key, Expires })
- Set expiration time (300 seconds to 7 days)
- Display expiration time to user
- Add "Regenerate URL" button when expired
Generate public URLs if bucket allows:
- Format: https://pub-XXXXX.r2.dev/filename
- Or custom domain: https://media.yourdomain.com/filename
Add custom domain support:
- Configure custom domain in R2 dashboard
- Allow user to set custom domain in config
- Generate URLs using custom domain
Create URL generator UI:
- Button to "Get Shareable Link"
- Modal showing generated URL
- Copy to clipboard button
- QR code for mobile sharing (optional)
URL format options:
- Direct file URL
- Markdown format: 
- HTML format: <img src="url" alt="alt">
- Gatsby/React format: <img src={url} alt="alt" />
Add URL expiration selector for presigned URLs:
- 5 minutes, 1 hour, 24 hours, 7 days
- Custom time input
Implement batch URL generation:
- Select multiple files
- Generate URLs for all
- Export as CSV or JSON
- Copy all as markdown list
Add CDN URL transformation (if using Cloudflare CDN):
- Image resizing parameters
- Format conversion (WebP)
- Quality adjustments
- Example: /cdn-cgi/image/width=800,format=auto/file.jpg
Store generated URLs history in localStorage for quick access.
Add "Copy as..." dropdown with different formats.
Validate URL before copying (test if accessible).Optimization
Cache file list and metadata locally
Implement local caching with localStorage for better performance.
Create cache management system:
- Store file list in localStorage after fetching from R2
- Cache key format: 'r2-media-cache-v1'
- Include timestamp for cache invalidation
Cache structure:
{
timestamp: Date.now(),
files: [...file list with metadata],
lastSync: timestamp,
version: 1
}
Implement cache loading:
- On app load, check localStorage first
- Display cached files immediately (instant UI)
- Fetch from R2 in background
- Compare and update if changes detected
Cache invalidation strategies:
- Time-based: expire after 5 minutes, 1 hour, etc.
- Manual: "Refresh" button to force reload
- Smart sync: use ETags to detect changes
- Automatic: sync on window focus
Implement sync indicator:
- Show "Syncing..." status
- Display last sync time
- Show offline/online status
Handle cache size limits:
- localStorage limit is ~5-10MB
- Store only essential data
- Compress if needed (JSON stringified)
- Implement LRU eviction if cache grows too large
Cache file metadata separately:
- Full metadata for recently viewed files
- Minimal metadata for others
Add offline support:
- Detect when offline (navigator.onLine)
- Show cached data with warning
- Queue uploads/deletes for when online
- Retry failed operations automatically
Cache presigned URLs:
- Store with expiration time
- Regenerate before expiration
- Don't cache if expiring within 5 minutes
Implement cache versioning:
- Invalidate old cache format when updating app
- Migrate old cache to new format if possible
Add cache clear functionality:
- Settings button to clear cache
- Clear cache on logout (if adding auth)
Debug cache in development:
- Console log cache hits/misses
- Add cache viewer in UI (for debugging)
Test cache behavior:
- Works offline
- Syncs when coming online
- Updates when files change
- Doesn't show stale dataCompress and resize before upload
Implement client-side image optimization before uploading to R2. Add image processing library: - Use browser Canvas API (native, no dependencies) - Or add library like browser-image-compression from CDN Create image optimizer function: - Input: File object - Output: Optimized File/Blob Implement resize functionality: - Max width/height options (e.g., 1920px, 2400px) - Maintain aspect ratio - Use Canvas API to draw resized image: * Create canvas element * Get 2D context * Calculate new dimensions * drawImage() with new size Implement compression: - Use canvas.toBlob() with quality parameter - JPEG: quality 0.8-0.9 (80-90%) - PNG: may not compress much (use WebP instead) - WebP: quality 0.8 with best compression Add format conversion: - Option to convert PNG to JPEG or WebP - Convert transparent backgrounds to white for JPEG - Keep transparency for WebP/PNG Create optimization settings UI: - Toggle: "Optimize images before upload" - Slider: Max dimension (1000px - 4000px) - Slider: Quality (60% - 100%) - Dropdown: Output format (Original, JPEG, WebP) - Save settings to localStorage Show optimization preview: - Before/after file size comparison - Visual quality preview (side-by-side) - Compression ratio percentage - Estimated upload time savings Process images on file selection: - Check if file is image - Apply optimization if enabled - Update file size display - Keep original filename with optimized extension Handle edge cases: - Very large images (>10MB, >5000px) - Animated GIFs (don't optimize, or warn user) - SVG files (don't process, already optimized) - Small images (skip if under certain size) Add batch optimization: - Process multiple images in parallel - Show progress: "Optimizing 3/10 images..." - Use Web Workers for better performance (advanced) Generate thumbnails during optimization: - Create small preview (200px) - Upload both full and thumbnail versions - Use thumbnail for file list display Add EXIF data handling: - Strip EXIF data for privacy (default) - Option to preserve EXIF data - Use EXIF.js library if needed Show optimization stats: - Total space saved - Average compression ratio - Total optimized images count Error handling: - Fallback to original if optimization fails - Warn if image quality too degraded - Handle browser compatibility issues
Polish
Add dark mode and responsive design
Add UI polish and improve user experience. Implement dark mode: - Add toggle button in header/settings - Store preference in localStorage - Use CSS custom properties for colors: * --bg-primary, --bg-secondary * --text-primary, --text-secondary * --accent-color, --border-color - Toggle .dark class on root element - Smooth transition between modes - Respect system preference: prefers-color-scheme Make fully responsive: - Mobile-first design approach - Breakpoints: 640px, 768px, 1024px, 1280px - Grid adjusts: 1 column (mobile), 2 (tablet), 3-4 (desktop) - Upload zone scales with screen size - Modals full-screen on mobile - Touch-friendly button sizes (44px minimum) Add smooth animations: - Fade in files as they load - Slide in/out for modals - Hover scale effects on thumbnails - Upload progress animations - Skeleton loaders while loading - Use CSS transitions and transforms Improve drag-and-drop UX: - Clear visual feedback on dragover - Animated dashed border - "Drop files here" message appears - Highlight drop zone - Show file count while dragging Add loading states everywhere: - Skeleton screens for file grid while loading - Spinners for long operations - Progress bars for uploads - Shimmer effect for loading thumbnails Implement error handling UI: - Toast notifications for errors/success - Error boundaries for graceful degradation - Retry buttons for failed operations - Helpful error messages (not technical jargon) Add empty states: - Friendly message when bucket is empty - Call-to-action to upload first file - Illustration or icon - Quick start guide Improve accessibility: - Proper ARIA labels - Keyboard navigation support - Focus visible styles - Alt text for images - Screen reader announcements - Sufficient color contrast Add keyboard shortcuts: - Ctrl/Cmd + U: Upload - Escape: Close modal - Arrow keys: Navigate files - Delete: Delete selected - Ctrl/Cmd + A: Select all - Show keyboard shortcuts help (Ctrl+/) Add settings panel: - Dark mode toggle - Grid/list view preference - Default upload settings - Cache management - API credential update - Export/import settings Polish small details: - Favicon and app icon - Loading spinner style - Cursor changes on hover - Smooth scroll behavior - Nice fonts (Inter, System UI) - Consistent spacing and sizing Add copy-to-clipboard feedback: - "Copied!" tooltip or toast - Icon changes briefly - Subtle animation Test on multiple devices: - iOS Safari - Android Chrome - Desktop browsers - Different screen sizes
Security
Implement best practices for credentials
Implement security best practices for the media manager. Secure credential management: - Never commit credentials to git - Add config.js to .gitignore - Create config.example.js template without real credentials - Show warning if default credentials detected - Add instructions for setting up credentials Validate all user inputs: - Sanitize filenames (remove special characters) - Limit filename length (max 255 characters) - Check file extensions against allowlist - Validate file size before upload - Prevent path traversal (../, absolute paths) Implement file type validation: - Check MIME type from file.type - Verify file magic bytes (header) for images - Reject executable files (.exe, .sh, .bat) - Allowlist: images, videos, documents only - Show clear error for blocked types Content Security Policy (CSP): - Add CSP meta tag in HTML - Restrict script sources - Allow R2 endpoints for images - Prevent inline scripts (use nonce if needed) Prevent XSS attacks: - Never use innerHTML with user input - Use textContent for filenames/descriptions - Sanitize metadata before displaying - Escape HTML in user-generated content File upload security: - Generate unique filenames (UUID + timestamp) - Prevent overwriting existing files - Scan filenames for malicious content - Limit concurrent uploads - Implement rate limiting (client-side) S3 API security: - Use least-privilege API tokens - Set expiration on presigned URLs - Use HTTPS only (enforce) - Don't log credentials in console - Rotate API keys periodically Prevent sensitive data exposure: - Don't expose full file paths - Sanitize error messages (don't leak credentials) - Remove debug logs in production - Don't display raw API responses Session security (if adding auth): - Use secure session storage - Implement logout functionality - Clear cache on logout - Auto-logout after inactivity Add security headers: - X-Content-Type-Options: nosniff - X-Frame-Options: DENY - Referrer-Policy: strict-origin-when-cross-origin Implement audit logging: - Log uploads, deletes, access (locally) - Store in localStorage with timestamps - Add "Activity Log" view - Clear old logs periodically Security for local development only: - Add warning banner: "LOCAL DEV ONLY" - Don't deploy to public servers - Add robots.txt to prevent indexing - Consider basic auth if sharing locally Add security checklist: - Remind user to rotate API keys - Check CORS configuration - Verify bucket permissions - Review public access settings Document security considerations: - README with security notes - Comment about credential storage - Instructions for secure setup - Warning about local-only use
Testing Your Web Development Web Application
Validate all functionality works
Test all functionality of the R2 media manager. Test file upload: - Single file upload works - Multiple file upload works - Drag and drop works - Click to browse works - Large files upload (100MB+) - Upload progress shows correctly - Cancellation works (if implemented) - Duplicate filenames handled - Special characters in filenames - Very long filenames Test file listing: - Files display correctly after upload - Thumbnails load for images - File metadata shows (size, date) - Empty bucket shows appropriate message - Large number of files (100+) - Pagination works (if implemented) - Refresh updates list - Sorting works Test file preview: - Images display in modal - Download works - Close modal works - Keyboard navigation works - Different image formats (JPG, PNG, WebP) - Large images load - Broken images handled gracefully Test file deletion: - Single delete works - Confirmation prompt appears - File removed from list - Multiple delete works (if implemented) - Delete handles errors gracefully - Can't delete twice Test metadata management: - Adding metadata works - Updating metadata works - Searching by metadata works - Tags display correctly - Special characters in metadata Test URL generation: - Presigned URLs generated correctly - URLs work (test by opening) - Copy to clipboard works - Different formats available - URLs expire properly Test caching: - Files load from cache on refresh - Cache updates when files change - Manual refresh works - Cache expires after timeout - Works offline (shows cached data) Test image optimization: - Images compressed before upload - Quality settings work - Size reduction visible - Original quality preserved well - Different formats work Test UI: - Dark mode toggles correctly - Responsive on mobile - Buttons all work - Animations smooth - No console errors - Loading states show Test error handling: - Invalid credentials show error - Network errors handled - Invalid files rejected - Helpful error messages - Retry works after error Test edge cases: - Empty bucket - Very slow network - Lost connection during upload - Browser refresh during upload - Multiple tabs open - LocalStorage full - CORS errors - Invalid R2 configuration Test browser compatibility: - Chrome/Edge (Chromium) - Firefox - Safari (Mac/iOS) - Mobile browsers Test security: - Credentials not in console - Config.js in gitignore - No XSS vulnerabilities - File validation works Create testing checklist document: - List all features - Check boxes for each test - Note any issues found - Track resolution Document known issues: - Limitations - Browser-specific bugs - Workarounds - Future improvements
Documentation
Write setup and usage guide
Create comprehensive documentation for the R2 media manager. Create README.md with: Project overview: - What it does (manage Cloudflare R2 media) - Key features list - Use cases - Screenshot or demo GIF Prerequisites: - Cloudflare account with R2 enabled - API token with R2 permissions - Modern web browser - Text editor Setup instructions: 1. Clone/download project 2. Create R2 bucket in Cloudflare 3. Generate API credentials 4. Copy config.example.js to config.js 5. Fill in credentials 6. Open index.html in browser (or use local server) Configuration guide: - How to get Account ID - How to generate API token - How to configure CORS - How to set up custom domain (optional) Usage guide: - How to upload files (drag-drop, click) - How to view/preview files - How to delete files - How to generate shareable URLs - How to search and filter - How to manage metadata Features documentation: - File upload with progress - File listing and browsing - Image preview and download - Batch operations - URL generation - Dark mode - Caching Troubleshooting section: - Common errors and solutions: * CORS errors → check CORS config * 403 Forbidden → check API permissions * Files not loading → check credentials * Upload fails → check file size, CORS - How to check browser console - How to verify R2 connectivity - How to clear cache Security notes: - Keep config.js private - Don't commit credentials - Local development only warning - Rotate API keys regularly Limitations: - Browser file size limits - localStorage size limits - No multi-user support - Local only (not production-ready) Advanced configuration: - Custom domains - Image optimization settings - Cache configuration - Keyboard shortcuts list Contributing (if open source): - How to contribute - Code style guide - Pull request process License: - Choose appropriate license (MIT, etc.) Create separate docs: SETUP.md - Detailed setup guide - Step-by-step with screenshots - Cloudflare dashboard walkthrough - Credential generation guide TROUBLESHOOTING.md - Common issues - Error messages and solutions - FAQ section - How to report bugs API.md - R2 API reference - S3-compatible API docs - Code examples - Common operations Add inline code comments: - Explain complex logic - Document function parameters - Add TODO comments for future work Create video tutorial (optional): - Screen recording of setup - Demo of key features - Upload to YouTube Write blog post (for tutorial): - Why use R2 for media - Benefits over S3 - Cost comparison - Use cases
