Skip to content

qwe7002/sliverimage

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cf-image-host

A serverless image hosting system (图床) that runs entirely on Cloudflare Workers, with a pluggable storage layer:

  • Cloudflare R2 — via the native bucket binding, zero egress fees.
  • Self-hosted S3-compatible — MinIO, Ceph RGW, Garage, SeaweedFS, Backblaze B2 (S3 API), AWS S3, … via AWS Signature V4 signed fetch calls.

Switch backends with a single config variable — the rest of the app is storage-agnostic.

Features

  • 🖥️ Desktop web console built with React + shadcn/ui + Tailwind — drag & drop / click / paste upload, a responsive image library, copy URL / Markdown, and delete with confirmation. Served as static assets by the same Worker.
  • 📤 Upload via raw body or multipart/form-data
  • 🔗 Returns a clean public URL
  • 🖼️ Streams images back with Cache-Control, ETag, Range and conditional-request (If-None-Match304) support
  • 🔑 Token auth on upload / delete / list; public read
  • 🧮 Two key strategies: random (date-prefixed) or hash (content-addressed, auto-deduplicating)
  • ✅ MIME allow-list and max-size enforcement
  • 📦 No runtime dependencies in the Worker — SigV4 is implemented with Web Crypto

Project layout

src/                  # Cloudflare Worker (API + storage + asset routing)
  index.ts            # Router + request handlers
  types.ts            # Env bindings + Storage interface
  utils.ts            # Key generation, MIME helpers, auth compare, responses
  sigv4.ts            # Dependency-free AWS Signature V4 signer (Web Crypto)
  storage/
    index.ts          # Backend factory (reads STORAGE_BACKEND)
    r2.ts             # Cloudflare R2 backend
    s3.ts             # S3-compatible backend
web/                  # React + shadcn/ui dashboard (Vite), built to web/dist
  src/
    App.tsx           # Dashboard layout
    components/        # Uploader, Gallery + shadcn/ui primitives
    lib/api.ts        # Typed client for the Worker API
wrangler.toml         # Worker config, bindings, static assets, vars

Quick start

npm install                       # Worker deps

# Set the auth token (required for uploads)
npx wrangler secret put AUTH_TOKEN

npm run deploy builds the dashboard (web/) and deploys the Worker together — you don't need to build web/ by hand. To build just the UI: npm run build:web.

Option A — Cloudflare R2

  1. Create a bucket:

    npx wrangler r2 bucket create image-host
  2. In wrangler.toml keep:

    [vars]
    STORAGE_BACKEND = "r2"
    
    [[r2_buckets]]
    binding = "IMAGES"
    bucket_name = "image-host"
  3. Deploy:

    npm run deploy

Option B — Self-hosted S3-compatible (e.g. MinIO)

  1. In wrangler.toml set the backend and endpoint:

    [vars]
    STORAGE_BACKEND = "s3"
    S3_ENDPOINT = "https://s3.example.com"   # your MinIO / S3 endpoint
    S3_REGION = "us-east-1"
    S3_BUCKET = "images"
    S3_FORCE_PATH_STYLE = "true"             # required for MinIO / most self-hosted

    You can comment out the [[r2_buckets]] block when using S3 only.

  2. Provide credentials as secrets:

    npx wrangler secret put S3_ACCESS_KEY_ID
    npx wrangler secret put S3_SECRET_ACCESS_KEY
  3. Deploy:

    npm run deploy

Path-style vs virtual-hosted: self-hosted servers (MinIO, Garage, …) almost always need S3_FORCE_PATH_STYLE = "true". Set it to "false" only if your endpoint is set up for bucket.host virtual-hosted addressing.

Local development

Two options:

# 1) Full stack against the real Worker runtime (build UI once, then serve):
npm run build:web
cp .dev.vars.example .dev.vars    # add AUTH_TOKEN (and S3 creds if using S3)
npm run dev                       # http://localhost:8787  (UI + API)

# 2) Hot-reloading UI dev server (proxies API to `wrangler dev` on :8787):
npm run dev                       # terminal A — the Worker/API
npm --prefix web run dev          # terminal B — Vite on http://localhost:5173

HTTP API

Method Path Auth Description
GET / shadcn dashboard (SPA)
GET /health Health check + active backend
POST /upload Upload an image, returns JSON with public URL
GET /i/<key> Serve the image (Range / ETag aware)
HEAD /i/<key> Metadata only
DELETE /i/<key> Delete the image
GET /api/list List objects (?prefix=&limit=&cursor=)

Auth is a bearer token: Authorization: Bearer <AUTH_TOKEN> (a ?token= query param is also accepted). Served images live under the /i/ prefix so they never collide with the dashboard's client-side routes.

Upload examples

Raw body (content-type describes the image):

curl -X POST https://img.example.com/upload \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: image/png" \
  --data-binary @cat.png

Multipart form:

curl -X POST https://img.example.com/upload \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -F "file=@cat.png"

Response:

{
  "ok": true,
  "key": "2026/07/x7k2p9qz3ab1.png",
  "url": "https://img.example.com/i/2026/07/x7k2p9qz3ab1.png",
  "size": 20481,
  "contentType": "image/png",
  "etag": "\"...\""
}

Delete

curl -X DELETE "https://img.example.com/i/2026/07/x7k2p9qz3ab1.png" \
  -H "Authorization: Bearer $AUTH_TOKEN"

Configuration reference

Variable Backend Default Description
STORAGE_BACKEND both r2 r2 or s3
MAX_SIZE both 10485760 (10 MiB) Max upload size in bytes
ALLOWED_TYPES both common image types Comma-separated MIME allow-list, or *
KEY_STRATEGY both random random (date-prefixed) or hash (content-address)
CACHE_MAX_AGE both 31536000 Cache-Control max-age (seconds) for served images
PUBLIC_BASE_URL both request origin Base URL used when building returned links
AUTH_TOKEN both — (secret) Bearer token for write operations
S3_ENDPOINT s3 S3 endpoint URL
S3_REGION s3 us-east-1 Signing region
S3_BUCKET s3 Bucket name
S3_ACCESS_KEY_ID s3 — (secret) Access key
S3_SECRET_ACCESS_KEY s3 — (secret) Secret key
S3_FORCE_PATH_STYLE s3 true Path-style addressing (needed for self-hosted)

Notes

  • Object keys are validated against a safe charset, so path traversal is not possible via the serve/delete routes.
  • The S3 backend signs the payload (SHA-256), the most broadly compatible SigV4 mode; uploads are buffered in the Worker (bounded by MAX_SIZE) so a correct Content-Length is always sent.
  • Serving is a signed pass-through for S3 and a native binding read for R2; both honour Range and conditional requests.

License

BSD-3-Clause — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages