Skip to content

Kenth06/CMS

Repository files navigation

CloudCMS - Personal CMS for Cloudflare Workers

A lightweight, personal content management system built on Cloudflare's edge platform. Designed for managing content across your personal projects, portfolios, and websites.

Features

  • Edge-Native: Runs entirely on Cloudflare Workers, D1, and R2
  • Polymorphic Content: Flexible content modeling with JSON storage
  • Media Management: Full-featured media library with R2 storage
  • Version Control: Track content history and restore previous versions
  • Webhooks: Event-driven integrations with external services
  • API Keys: Programmatic access for headless CMS use cases
  • Audit Logging: Complete audit trail of all changes
  • Globals: Singleton documents for site-wide content
  • Multi-language: Locale support for internationalization

Architecture

  • API: Hono-based REST API running on Cloudflare Workers
  • Database: D1 (SQLite) for structured data
  • Storage: R2 for media files
  • Auth: JWT-based authentication with secure sessions
  • Packages:
    • @cloudcms/api - Main API worker
    • @cloudcms/auth - Authentication utilities
    • @cloudcms/db - Database layer with Drizzle ORM
    • @cloudcms/types - Shared TypeScript types

Getting Started

Prerequisites

  • Node.js 20+
  • pnpm 9+
  • Cloudflare account
  • Wrangler CLI

Installation

  1. Install dependencies:
pnpm install
  1. Create your D1 database:
# For local development
wrangler d1 create cloudcms-db --local

# For production
wrangler d1 create cloudcms-db
# Note the database_id from the output
  1. Update apps/api/wrangler.jsonc with your production database ID:
"env": {
  "production": {
    "d1_databases": [{
      "database_id": "your-database-id-here"
    }]
  }
}
  1. Create your R2 bucket:
# For production
wrangler r2 bucket create cloudcms-media
  1. Run migrations:
# Local development
pnpm db:migrate

# Production
pnpm db:migrate:prod
  1. Set production secrets:
wrangler secret put JWT_SECRET --env production
# Enter a strong random string (e.g., openssl rand -base64 32)

wrangler secret put COOKIE_DOMAIN --env production
# Enter your domain (e.g., yourdomain.com)

Development

Start the development server:

pnpm dev:api

The API will be available at http://localhost:8787

Initial Setup

On first run, create your admin account:

curl -X POST http://localhost:8787/api/setup \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@example.com",
    "password": "your-secure-password",
    "name": "Admin User"
  }'

Deployment

Deploy to Cloudflare:

# Deploy to production
cd apps/api
pnpm deploy

API Reference

Authentication

Login

POST /api/auth/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "password"
}

Get Current User

GET /api/auth/me
Cookie: auth_token=<token>

Collections

List Entries

GET /api/collections/:slug?page=1&limit=10&status=published

Create Entry

POST /api/collections/:slug
Content-Type: application/json

{
  "title": "My Post",
  "content": "Post content...",
  "_status": "published"
}

Update Entry

PATCH /api/collections/:slug/:id
Content-Type: application/json

{
  "title": "Updated Title"
}

Delete Entry

DELETE /api/collections/:slug/:id?hard=false

Media

Upload Media

POST /api/media/upload
Content-Type: multipart/form-data

file: <file>
altText: "Image description"
folderId: "optional-folder-id"

List Media

GET /api/media?page=1&limit=20&folderId=<id>&mimeType=image/jpeg

Globals

Get Global

GET /api/globals/:slug?locale=en

Update Global

PATCH /api/globals/:slug?locale=en
Content-Type: application/json

{
  "siteName": "My Site",
  "description": "Site description"
}

Admin

List Users

GET /api/admin/users

Create API Key

POST /api/admin/api-keys
Content-Type: application/json

{
  "name": "My API Key",
  "scopes": ["*"],
  "expiresAt": "2025-12-31T23:59:59Z"
}

Manage Settings

GET /api/admin/settings
PUT /api/admin/settings/:key

Webhooks

Create Webhook

POST /api/webhooks
Content-Type: application/json

{
  "name": "My Webhook",
  "url": "https://example.com/webhook",
  "events": ["entry.create", "entry.update"],
  "collections": ["posts"]
}

Database Schema

The CMS uses a polymorphic content model where all collection entries are stored in a single entries table with JSON data. This provides maximum flexibility without requiring schema migrations for content changes.

Key Tables

  • users - User accounts and authentication
  • sessions - Active user sessions
  • api_keys - API authentication tokens
  • entries - All collection content (polymorphic)
  • entry_versions - Version history
  • global_values - Singleton documents
  • media - Media metadata
  • folders - Media organization
  • webhooks - Webhook configurations
  • audit_logs - Complete audit trail
  • settings - System configuration

Scripts

  • pnpm dev - Start all development servers
  • pnpm dev:api - Start API worker only
  • pnpm build - Build all packages
  • pnpm typecheck - Type check all packages
  • pnpm db:migrate - Run migrations (local)
  • pnpm db:migrate:prod - Run migrations (production)
  • pnpm db:studio - Open Drizzle Studio

Security

  • Passwords are hashed with PBKDF2 (600,000 iterations)
  • JWTs are signed with HS256
  • Sessions are stored in HTTP-only cookies
  • API keys are hashed with SHA-256
  • Webhook payloads are signed with HMAC-SHA256
  • Rate limiting via Cloudflare's built-in protection
  • Audit logging for all modifications

Architecture Decisions

Why Cloudflare Workers?

  • Global Edge Network: Low latency worldwide
  • Serverless: No infrastructure to manage
  • Cost Effective: Generous free tier, pay-per-use
  • Integrated Stack: D1, R2, KV all in one platform

Why Polymorphic Storage?

  • Flexibility: Add fields without migrations
  • Simplicity: One table for all content
  • Performance: Optimized for read-heavy workloads
  • Type Safety: TypeScript types at application level

No Multi-Tenancy

This is designed as a personal CMS. If you need multi-tenancy, consider:

  • Running multiple workers (one per project)
  • Using namespace prefixes in collection slugs
  • Forking and extending the codebase

Roadmap

  • Admin UI (Next.js)
  • Collection config management
  • Field validation
  • Rich text editor
  • Image transformations
  • Search/filtering
  • Scheduled publishing
  • Export/import tools
  • CLI tools

Contributing

This is a personal project, but suggestions and PRs are welcome!

License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages