Page loadedSkip to main content

NBTCA Photo

The association’s photo album at photo.nbtca.space. It is a fork of exif-photo-blog that keeps its interface and replaces its Next.js server and Postgres database with a static app and one Cloudflare Worker.

View source
  • Edge
  • Porting
  • Performance

The problem

The club wanted a shared album for event photos, visible only to members, with no server to run and no bill. Upstream needs a Node server and Postgres.

How it works

  • Vite builds the interface as static files, with a small router and stand-ins for the next/* modules, so server components and actions run in the browser.
  • The Postgres schema and queries are ported to D1. Photos live in a private R2 bucket and are served only to signed-in members.
  • The browser re-encodes every photo to WebP before upload, so stored files carry no EXIF or GPS data. Camera settings and colours are read first and kept in D1.
  • Cloudflare Access signs members in through Logto, and the Worker verifies the token on every request, so a request that bypasses Access gets nothing.
  • Cache API does not run behind Access, so read results are cached in a D1 table. A hit reads two rows by primary key, and any write moves a cache epoch that invalidates every entry at once.
  • A storage ledger kept by triggers enforces per-member and total quotas without scanning files, which keeps the bucket inside the R2 free tier.

Built with

  • TypeScript
  • React
  • Vite
  • Cloudflare Workers
  • D1
  • R2
  • Cloudflare Access