Skip to main content

Cloudflare R2 (resource media)

Resource media is large and public, so it lives in the emaw-public R2 bucket (served from r2.everymanawarrior.cc with a 1-year edge cache and no egress fees) rather than in Azure Blob. Cover images stay in Azure Blob.

Coderesources/r2.py (presign), resources/staff_views.py, resources/views.py; admin app src/api/directUpload.ts
Runbooksresources/README.md, docs/r2-setup.md (bucket public access, CORS JSON, cache rule, troubleshooting)
EnvR2_ACCOUNT_ID, R2_BUCKET, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY (API token with Object Read & Write on the bucket), R2_PUBLIC_BASE_URL (default https://r2.everymanawarrior.cc)

Upload flow​

  • The backend never proxies the bytes; the browser uploads directly, so the bucket's CORS rule must allow PUT from the admin origins.
  • Public payloads carry the full media.url; the website never builds R2 URLs and needs no R2 env var.
  • Until all four credentials are set, the presign endpoint returns 503 and the admin app shows an upload error.

Keys are immutable​

Replacing a file uploads a new key. Never overwrite a published key: the edge cache keeps serving the old bytes for up to a year, and purging per-URL is manual. The admin app follows this automatically; the rule matters if you touch the bucket with wrangler r2 object put by hand.

Changing the public domain​

Set R2_PUBLIC_BASE_URL; existing resources are re-serialised with the new base because only the key is stored.