-
Notifications
You must be signed in to change notification settings - Fork 10
Development Guide
Complete guide for local development and contributing to R2-Manager-Worker.
Ensure you have installed:
- Node.js 20+ and npm
- Wrangler CLI
- Git
git clone https://github.com/neverinfamous/R2-Manager-Worker.git
cd R2-Manager-Worker
npm installCreate a .env file for local development:
cp .env.example .envEdit .env:
VITE_WORKER_API=http://localhost:8787
You need two terminal windows running simultaneously:
Terminal 1: Vite Dev Server (Frontend)
npm run dev- Frontend available at:
http://localhost:5173 - Hot module replacement (HMR) enabled
- React Fast Refresh for instant updates
- Watches
src/directory for changes
Terminal 2: Wrangler Worker (Backend)
npx wrangler dev --config wrangler.dev.toml --local- Worker API available at:
http://localhost:8787 - Automatic reload on
worker/code changes - Uses local bindings with mock data (no remote resources)
-
wrangler.dev.tomlskips the frontend build step for faster startup
- Authentication is disabled on localhost during development
- JWT validation is automatically skipped for requests from localhost
- CORS is configured to allow
http://localhost:5173origin with credentials - No Cloudflare secrets required - returns mock bucket data for local testing
- The
.envfile is already configured to point tohttp://localhost:8787 - Mock data includes a single
dev-bucketfor UI testing - To test with real R2 buckets, deploy to production and test there
The following operations return simulated success responses for UI testing:
- ✅ List buckets - Returns
dev-bucket - ✅ Create bucket - Simulates success
- ✅ Rename bucket - Simulates success
- ✅ List files - Returns empty array
- ✅ Upload files - Simulates success (files not stored)
- ✅ Create folders - Simulates success
Note: Files and folders are not actually stored. Local development is for UI/UX testing only. For full functionality, deploy to Cloudflare Workers.
Port already in use:
# Windows PowerShell: Kill processes on specific ports
$port5173 = Get-NetTCPConnection -LocalPort 5173 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique
$port8787 = Get-NetTCPConnection -LocalPort 8787 -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique
foreach ($p in $port5173) { taskkill /F /PID $p }
foreach ($p in $port8787) { taskkill /F /PID $p }Check if servers are running:
# Windows PowerShell
Get-NetTCPConnection -LocalPort 5173,8787 -ErrorAction SilentlyContinue | Select-Object LocalPort, StateWrangler won't start:
- Make sure you're using
wrangler.dev.tomlconfig - Check for TypeScript errors in
worker/index.ts - Try deleting
.wrangler/directory and restarting
Vite won't start:
- Check for port conflicts
- Delete
node_modules/.vite/cache - Restart with
npm run dev
The local development setup uses two separate servers:
-
Vite (Port 5173): Serves the React frontend with HMR
- Watches:
src/**/* - Config:
vite.config.tsand.env - No backend logic
- Watches:
-
Wrangler (Port 8787): Runs the Worker with local bindings
- Watches:
worker/**/* - Config:
wrangler.dev.toml - Handles API requests from frontend
- Watches:
CORS and Authentication Flow:
- Frontend sends requests to
http://localhost:8787/api/* - Worker detects localhost origin and skips JWT validation
- Worker sets CORS headers to allow
http://localhost:5173with credentials - No Cloudflare Access involved in local development
R2-Manager-Worker/
├── src/ # Frontend source code
│ ├── app.tsx # Main application component
│ ├── filegrid.tsx # File browser with grid/list views
│ ├── main.tsx # React entry point
│ ├── app.css # Global styles with CSS variables
│ ├── components/
│ │ └── ThemeToggle.tsx # Theme toggle button component
│ ├── contexts/
│ │ └── ThemeContext.tsx # Theme state management
│ ├── hooks/
│ │ └── useTheme.ts # Theme hook for components
│ ├── services/
│ │ ├── api.ts # HTTP client & API calls
│ │ └── auth.ts # Authentication utilities
│ └── styles/
│ ├── themes.css # CSS variable definitions
│ └── ThemeToggle.css # Theme toggle button styles
│
├── worker/ # Backend Worker code
│ ├── index.ts # Worker runtime & API endpoints
│ ├── routes/ # API route handlers
│ └── utils/ # Helper utilities
│
├── public/ # Static assets
│ ├── favicon.ico
│ ├── logo.png
│ └── manifest files
│
├── dist/ # Production build output
│
├── wrangler.toml.example # Wrangler config template
├── .env.example # Environment variables template
├── package.json # Project dependencies
├── vite.config.ts # Vite configuration
├── tsconfig.json # TypeScript configuration
└── eslint.config.js # ESLint configuration
- React - UI framework with latest features
- TypeScript - Type-safe JavaScript
- Vite - Fast build tool and dev server
- CSS - No framework, vanilla CSS for simplicity
- Cloudflare Workers - Serverless edge runtime
- TypeScript - Worker code is also type-safe
- R2 - Object storage (S3-compatible)
- JSZip - ZIP file creation for bulk downloads
- Wrangler - Cloudflare Workers CLI
- ESLint - Code linting
- Vite - Frontend bundling
Start dev server:
npm run devBuild for production:
npm run buildPreview production build:
npm run previewRun linter:
npm run lintStart local Worker:
npx wrangler devStart with remote bindings:
npx wrangler dev --remoteView Worker logs:
npx wrangler tailDeploy to Cloudflare:
npm run build && npx wrangler deployThe project uses ES modules throughout:
{
"type": "module"
}Important:
- All files use ES6
import/exportsyntax - No CommonJS
require()statements - Ensures compatibility with Vite 7+
- Prevents subtle syntax issues
-
Create a feature branch:
git checkout -b feature/your-feature-name
-
Make your changes in
src/orworker/ -
Test locally with both dev servers running
-
Run linter:
npm run lint
-
Build for production:
npm run build
-
Test production build:
npm run preview npx wrangler dev --remote
-
Commit changes:
git add . git commit -m "Description of changes"
-
Push to GitHub:
git push origin feature/your-feature-name
-
Create Pull Request on GitHub
Follow these conventions:
TypeScript:
- Use TypeScript strict mode
- Define interfaces for all API responses
- Use explicit return types for functions
- Prefer
constoverlet
React:
- Functional components only (no class components)
- Use hooks for state and side effects
- Keep components small and focused
- Extract reusable logic into custom hooks
CSS:
- Use BEM naming convention
- Mobile-first responsive design
- Avoid inline styles when possible
Comments:
- Use JSDoc comments for functions
- Explain complex logic
- Don't comment obvious code
To test with Cloudflare Access:
Option 1: Deploy and Test
- Deploy to production:
npx wrangler deploy - Test on your deployed domain
- JWT validation works in production
Option 2: Use Remote Worker
- Set
.envto point to deployed Worker:VITE_WORKER_API=https://your-worker.workers.dev - Run frontend locally:
npm run dev - API calls go to production Worker with JWT validation
Browser DevTools:
- Open DevTools (F12)
- Console tab - View logs and errors
- Network tab - Inspect API requests
- Application tab - Check cookies and storage
- React DevTools - Install browser extension
Enable verbose logging:
// In src/services/api.ts
console.log('API Request:', {
method,
url,
body
});View real-time logs:
npx wrangler tailAdd debug logging:
// In worker/index.ts
console.log('[Debug]', {
path: url.pathname,
method: request.method,
headers: Object.fromEntries(request.headers)
});View logs in dashboard:
- Go to Workers & Pages → Select your Worker
- Click Logs tab
- See real-time console output
Port already in use:
# Find and kill process using port 5173 or 8787
npx kill-port 5173
npx kill-port 8787Module not found:
# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm installWrangler authentication:
# Re-authenticate with Cloudflare
npx wrangler loginNote: R2 Manager no longer uses a database. All bucket operations use Cloudflare's R2 REST API directly.
- Add route handler in
worker/index.ts:
if (url.pathname.startsWith('/api/your-endpoint')) {
// Handle request
return new Response(JSON.stringify({ success: true }), {
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
});
}- Add API client method in
src/services/api.ts:
export async function yourNewEndpoint(param: string): Promise<Response> {
return apiRequest(`/your-endpoint/${param}`, {
method: 'GET'
});
}- Use in component:
import { yourNewEndpoint } from './services/api';
const data = await yourNewEndpoint('value');- Create component file:
// src/components/YourComponent.tsx
import React from 'react';
export function YourComponent() {
return (
<div className="your-component">
{/* Component content */}
</div>
);
}- Import and use:
import { YourComponent } from './components/YourComponent';
function App() {
return <YourComponent />;
}- Code splitting - Vite automatically splits chunks
-
Lazy loading - Use
React.lazy()for large components -
Memoization - Use
React.memo()to prevent re-renders - Debouncing - Debounce search and filter inputs
-
Streaming responses - Use
ReadableStreamfor large files - Caching - Implement cache headers for static content
- Pagination - Limit result sets and use cursors
- Rate limiting - Built-in delays prevent API throttling
- ✅ Code follows project style
- ✅ All tests pass (when tests exist)
- ✅ Linter passes:
npm run lint - ✅ Production build succeeds:
npm run build - ✅ Changes are documented
- ✅ Commit messages are clear
- Fork the repository
- Create a feature branch
- Make your changes
- Push to your fork
- Open a Pull Request with:
- Clear description of changes
- Screenshots (if UI changes)
- Related issue numbers
PRs will be reviewed for:
- Code quality and style
- TypeScript type safety
- Security implications
- Performance impact
- Documentation updates
R2 Bucket Manager includes a comprehensive theme system with light/dark mode support:
Components:
-
ThemeContext.tsx- React Context for theme state management -
useTheme.ts- Custom hook for accessing theme context -
ThemeToggle.tsx- UI component for theme switching -
themes.css- CSS custom properties for all colors -
app.css- Styles using CSS variables
How It Works:
-
ThemeProviderwraps the entire application inmain.tsx - Theme preference is stored in
localStorage(key:r2-manager-theme) - System preference is detected via
prefers-color-schememedia query - Theme is applied via
data-themeattribute on<html>element - All colors reference CSS custom properties (
var(--color-name))
Theme Modes:
-
system- Follows OS/browser preference (default) -
light- Force light mode -
dark- Force dark mode
Color Variables:
/* Light Theme */
--bg-primary: #ffffff
--text-primary: #111827
--accent-blue: #2563eb
/* Dark Theme */
--bg-primary: #111827
--text-primary: rgba(255, 255, 255, 0.87)
--accent-blue: #2563eb- Define in
themes.css:
:root[data-theme="light"] {
--my-color: #hexcode;
}
:root[data-theme="dark"] {
--my-color: #hexcode;
}- Use in
app.css:
.my-element {
background: var(--my-color);
}In Components:
import { useTheme } from '../hooks/useTheme'
function MyComponent() {
const { theme, resolvedTheme, setTheme } = useTheme()
// theme: 'light' | 'dark' | 'system'
// resolvedTheme: 'light' | 'dark'
// setTheme: (theme: ThemeMode) => void
}Cycle Through Themes:
const cycleTheme = () => {
if (theme === 'system') setTheme('light')
else if (theme === 'light') setTheme('dark')
else setTheme('system')
}Manual Testing:
- Click theme toggle button in header
- Verify smooth transition between themes
- Check localStorage persistence (reload page)
- Test system theme detection (change OS theme)
Browser DevTools:
- Open DevTools → Application → Local Storage
- Check for
r2-manager-themekey - Change OS theme: DevTools → Rendering → Emulate CSS prefers-color-scheme
All colors meet WCAG 2.1 AA standards:
- Normal text: 4.5:1 contrast ratio minimum
- Large text: 3:1 contrast ratio minimum
- Interactive elements: 3:1 contrast ratio minimum
Use online tools to verify:
- Update version in
package.json - Update
README.mdif needed - Create git tag:
git tag v1.x.x - Push tag:
git push origin v1.x.x - Deploy to production:
npx wrangler deploy
To add support for new file extensions in uploads, you need to modify three files:
Define the file type category and MIME types:
// Add new category to FILE_TYPES object
myCategory: {
maxSize: 50 * 1024 * 1024, // 50MB limit
description: 'My Category Files',
accept: [
'application/my-type',
'text/plain' // fallback for files without MIME type
]
}Then add extension mappings in getConfigByExtension():
'ext1': 'myCategory',
'ext2': 'myCategory',
'ext3': 'myCategory'Add a custom icon function to display the file type visually:
// Add before the generic document icon at the end
if (ext === 'ext1' || ext === 'ext2' || ext === 'ext3') {
return (
<svg xmlns="http://www.w3.org/2000/svg" className="file-type-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
{/* Your SVG icon here */}
</svg>
)
}Icon Guidelines:
- Use 24x24 viewBox
- Stroke-based design with
strokeWidth="1.5" - No fill on main elements (let CSS handle color)
- Keep design simple and recognizable at small sizes
Add your new category to the upload instructions list:
<li>My Category (EXT1, EXT2, EXT3) - up to 50MB</li>Keep entries:
- Alphabetical by category name
- Uppercase extension names
- Specific size limits per category
Complete list of supported file types and size limits:
- Archives (7Z, GZ, RAR, TAR, ZIP) - up to 500MB
- Audio (AAC, FLAC, M4A, MP3, OGG, OPUS, WAV) - up to 100MB
- Code (CSS, GO, HTML, Java, JS, Rust, TS, Python, etc.) - up to 10MB
- Config & Metadata (CONF, ENV, INI, JSON, JSONC, LOCK, TOML, etc.) - up to 10MB
- Data Formats (AVRO, FEATHER, NDJSON) - up to 50MB
- Databases (DB, PARQUET, SQL) - up to 50MB
- Dev Environment (Dockerfile, editorconfig, .gitignore, nvmrc, etc.) - up to 1MB
- Documents (CSV, Excel, Markdown, PDF, PowerPoint, TXT, Word, etc.) - up to 50MB
- Documentation (NFO) - up to 10MB
- Fonts (EOT, OTF, TTF, WOFF, WOFF2) - up to 10MB
- Images (AVIF, BMP, GIF, HEIC, JPG, PNG, PSD, SVG, WebP) - up to 15MB
- Jupyter Notebooks (.ipynb) - up to 10MB
- Videos (3GP, AVI, FLV, M4V, MKV, MOV, MP4, MPEG, OGG, WebM, WMV) - up to 500MB
Category Mapping:
| Category | Size Limit | Purpose | Extensions |
|---|---|---|---|
| archive | 500MB | Compressed files | 7z, gz, rar, tar, zip |
| audio | 100MB | Audio files | aac, flac, m4a, mp3, oga, ogg, opus, wav |
| code | 10MB | Source code | css, go, html, java, js, py, rb, rs, swift, ts, etc. |
| config | 10MB | Config files | conf, env, ini, json, jsonc, lock, toml |
| dataformat | 50MB | Data formats | avro, feather, ndjson |
| devenv | 1MB | Dev environment | dockerfile, editorconfig, gitignore, nvmrc, browserslistrc |
| document | 50MB | Docs & spreadsheets | csv, db, doc, docx, md, parquet, pdf, ppt, pptx, rtf, sqlite, txt, xls, xlsx |
| docs | 10MB | Documentation | nfo |
| font | 10MB | Font files | eot, otf, ttf, woff, woff2 |
| image | 15MB | Images | avif, bmp, gif, heic, jpg, jpeg, png, psd, svg, webp |
| video | 500MB | Videos | 3gp, 3g2, avi, flv, m4v, mkv, mov, mp4, mpeg, mpg, mpeg4, ogg, ogv, webm, wmv |
Never add these unsafe file types:
-
.exe,.dll,.bat,.cmd- Windows executables -
.sh,.bash,.zsh- Shell scripts -
.ps1- PowerShell scripts -
.jar,.class- Java executables -
.wasm- WebAssembly -
.msi- Windows installers
These can bypass Cloudflare Access or pose security risks.
- Make changes to all three files
- Run linter:
npm run lint - Build:
npm run build - Test locally:
npm run dev(both servers) - Upload a test file with the new extension
- Verify icon displays correctly
- Deploy:
npx wrangler deploy
- React 19 Documentation
- Vite Documentation
- TypeScript Handbook
- Cloudflare Workers Docs
- Wrangler Commands
Questions? Check the Troubleshooting page or open an issue on GitHub.
- Home - Documentation overview
- Quick Start Guide - Get up and running in minutes
- Installation & Setup - Complete deployment guide
- Configuration Reference - Environment variables and settings
- Upgrade Guide - Database schema migrations
- Bucket Management - Create, rename, delete buckets
- Object Lifecycles - Automate expiration and IA transitions ⭐ NEW
- Job History - Track bulk operations with audit trail ⭐ NEW
- Webhooks - Configure HTTP notifications for events ⭐ NEW
- AI Search - Semantic search with Cloudflare AI
- S3 Import - Migrate from AWS S3 to R2 ⭐ NEW
- Cross-Bucket Search - Search across all buckets with filters
- File Operations - Upload, download, move, copy, delete files
- Folder Management - Organize files hierarchically
- Signed URLs & Sharing - Generate secure shareable links
- Advanced Filtering - Filter by extension, size, and date
- Development Guide - Local setup and development workflow
- API Reference - Complete endpoint documentation
- Architecture Overview - Technical stack and design
- Authentication & Security - Zero Trust implementation
- JWT Validation - JWT token validation and verification
- Changelog - Release history and updates ⭐ NEW
- Troubleshooting - Common issues and solutions
- FAQ - Frequently asked questions
- Roadmap - Planned features and enhancements