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 sourceThe 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