A lightweight content management system built on Cloudflare Workers, using D1 for data storage, KV for caching, and R2 for media storage.
-
Internationalization (i18n) Management
- Multi-language support with fallback to default language
- Inline editing of translations
- Section-based organization
- Cached API endpoints for performance
-
Media Management
- Upload files to R2 storage
- Automatic filename sanitization (kebab-case)
- Section-based organization
- Direct streaming from R2
-
Authentication
- Email/password authentication using Better Auth
- Session management with D1 storage
- Protected routes
- Framework: React Router v7
- UI: Tailwind CSS + shadcn/ui components
- Database: Cloudflare D1 (SQLite)
- Cache: Cloudflare KV
- Storage: Cloudflare R2
- Authentication: Better Auth
- Runtime: Cloudflare Workers
npm install
Create a .dev.vars
file for local development:
AUTH_SECRET=your-secret-key-here
ADMIN_SIGNUP_PASSWORD=your-admin-signup-secret
For production, set these as Cloudflare secrets:
npx wrangler secret put AUTH_SECRET
npx wrangler secret put ADMIN_SIGNUP_PASSWORD
Apply the database migrations:
npx wrangler d1 migrations apply edgecms-db --local
For production:
npx wrangler d1 migrations apply edgecms-db
Generate TypeScript types:
npm run typecheck
Start the development server:
npm run dev
Deploy to Cloudflare Workers:
npm run deploy
/edge-cms/sign-in
- Authentication page/edge-cms/i18n
- Translation management interface/edge-cms/media
- Media upload and management
/edge-cms/public/i18n/:locale.json
- Get translations for a locale (cached)/edge-cms/public/media/:filename
- Serve media files from R2
- Sign in at
/edge-cms/sign-in
- Navigate to
/edge-cms/i18n
- Add languages and sections as needed
- Add translation keys
- Edit translations inline - changes save automatically
Fetch translations from your application:
const response = await fetch('/edge-cms/public/i18n/en.json');
const translations = await response.json();
- Navigate to
/edge-cms/media
- Upload files using the upload button
- Files are automatically renamed to kebab-case
- Assign files to sections for organization
Reference media files directly:
<img src="/edge-cms/public/media/my-image.jpg" alt="My Image" />
locale
- Language code (e.g., 'en', 'es')default
- Whether this is the default/fallback language
name
- Section identifier for grouping content
key
- Translation keylanguage
- Language codevalue
- Translated textsection
- Optional section reference
filename
- Sanitized filenamemimeType
- File MIME typesizeBytes
- File sizesection
- Optional section reference
This CMS is designed to be deployed alongside your existing Cloudflare Workers
application. Simply configure the /edge-cms
routes in your wrangler
configuration to serve this application on your domain.