A personal portfolio site for Ethan Hathaway showcasing things I make — woodworking, baking, cooking, and other crafts.
bun install
# Local dev without platform bindings
bun run dev:vite
# Local dev with Cloudflare bindings (D1, assets, etc.)
bun run dev
# Remote dev with Cloudflare bindings (writes to remote D1/R2 preview bucket)
bun run dev:remote
.env (local dev): DATABASE_URL, PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY.env.local (wrangler dev): local bindings; leave PUBLIC_R2_BASE_URL empty to use proxy URLs.env.remote (wrangler dev –remote): set PUBLIC_R2_BASE_URL to the preview bucket public URL.env.test (tests): same Clerk dev keys and DATABASE_URL as .env.env.template: copy for new environments; includes E2E_BASE_URL and CLERK_TEST_TOKEN for integration testsExample .env.local:
DATABASE_URL=file:local.db
PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_your_key
CLERK_SECRET_KEY=sk_test_your_key
PUBLIC_R2_BASE_URL=
Example .env.remote:
DATABASE_URL=file:local.db
PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_your_key
CLERK_SECRET_KEY=sk_test_your_key
PUBLIC_R2_BASE_URL=https://<account-id>.r2.cloudflarestorage.com/portfolio-artifacts-22-dev
bun run build
bun run preview
bun run deploy
Run a local worker, then execute the API integration test. This requires a real Clerk session token.
bunx wrangler dev --port 8787
E2E_BASE_URL=http://localhost:8787 CLERK_TEST_TOKEN=... bunx playwright test e2e/categories.api.spec.ts
/api endpoints (no cross-origin)./api/projects endpoints are available for CRUD; reads are public for published items./api/uploads/artifacts handles image uploads to R2 and returns a URL for artifacts.DB in wrangler.tomlsrc/lib/server/db/index.ts (getDb)event.locals.db is set in src/hooks.server.tssrc/lib/server/db/schema.tsdocs/api-spec.mdARTIFACTS in wrangler.tomlpreview_bucket_name for remote dev (wrangler dev --remote)PUBLIC_R2_BASE_URL (leave empty in .env.local to use proxy URLs)# Generate SQL from the schema
bun run db:generate
# Apply a generated migration to D1
bunx wrangler d1 execute portfolio-db --file=./drizzle/<migration>.sql