This app uses:
- React + Vite for the frontend
- Convex for data and auth (Google + Anonymous)
- A Cloudflare Worker (Hono) for image uploads to R2
- Optional Unsplash integration for image search
- Node.js 18+ and pnpm
- A Convex project (free tier is fine)
- A Google OAuth Client (for auth)
- Cloudflare account with:
- Wrangler installed (
npm i -g wrangler
or usepnpm dlx wrangler
) - An R2 bucket and API tokens
- Wrangler installed (
Create .env
in the repo root (copy from env.example
) and add:
VITE_CONVEX_URL= # Convex deployment URL (shown by `npx convex dev`)
VITE_UNSPLASH_ACCESS_KEY= # optional, for Unsplash search
Worker secrets and vars are managed by Wrangler. You will set these via commands below:
R2_ENDPOINT
— your account ID (or endpoint subdomain) for R2, without protocolR2_ACCESS_KEY_ID
— R2 Access Key IDR2_SECRET_ACCESS_KEY
— R2 Secret Access KeyR2_BUCKET_NAME
— name of your R2 bucketENVIRONMENT
—development
orproduction
(already set inworkers/wrangler.jsonc
asdevelopment
)
pnpm install
You can run the three parts side-by-side.
- Make sure you are logged in and linked to a Convex project.
npx convex dev
This serves Convex locally and prints a URL. Copy that into your .env
as VITE_CONVEX_URL
.
The worker listens for image uploads and writes them to R2.
- From
workers/
directory, install and run dev:
cd workers
pnpm install
pnpm run dev
This starts the worker on a local port (Wrangler chooses it; in code we use POST /upload-file
). The frontend currently points to http://localhost:65214/upload-file
by default. If Wrangler chooses a different port, update the URL in src/components/ImagePicker.tsx
or set a fixed port with wrangler dev --port 65214
.
- Set worker secrets (one-time, in
workers/
):
pnpm wrangler secret put R2_ACCESS_KEY_ID
pnpm wrangler secret put R2_SECRET_ACCESS_KEY
- Set worker vars (either in
workers/wrangler.jsonc
or via CLI):
pnpm wrangler kv:namespace create dummy # not required; example pattern only
# Set vars (preferred via wrangler.jsonc "vars")
# or use: pnpm wrangler secret put R2_ENDPOINT; pnpm wrangler secret put R2_BUCKET_NAME
In code, the worker expects R2_ENDPOINT
, R2_ACCESS_KEY_ID
, R2_SECRET_ACCESS_KEY
, R2_BUCKET_NAME
.
In the repo root:
pnpm run dev
Vite runs on http://localhost:5173
.
- Frontend authenticates via Convex using Google or Anonymous. Convex routes are added in
convex/http.ts
, provider config is inconvex/auth.ts
andconvex/auth.config.ts
. - App initializes Convex client using
import.meta.env.VITE_CONVEX_URL
insrc/main.tsx
. - Image uploads: the frontend posts to the worker
POST /upload-file
, which stores the file in R2 using AWS S3-compatible API. Seeworkers/src/routes/UploadFile.ts
andworkers/src/index.ts
. - Unsplash: if
VITE_UNSPLASH_ACCESS_KEY
is set, the image picker can search Unsplash.
- Convex: deploy per Convex docs (
npx convex deploy
). UpdateVITE_CONVEX_URL
to the production URL. - Worker: from
workers/
runpnpm run deploy
to Cloudflare. Configure DNS/route as needed. - Frontend: run
pnpm run build
and hostdist/
(Vercel/Netlify/etc.). If using a custom worker domain for uploads, update the upload URL insrc/components/ImagePicker.tsx
.