API (apps/api/src/routes/admin.ts):
- GET /api/admin/me, /event, /uploads, /stats
- PATCH /api/admin/event for live config edits
- POST /api/admin/uploads/:id/{approve,reject}
- DELETE /api/admin/uploads/:id (also removes the object from R2)
- ZIP export and bulk import remain as 501 for the next iteration
Web (apps/web/src/routes/admin/Dashboard.tsx):
- Single-page dashboard with stats cards, event config form,
filterable uploads grid (all/pending/approved/rejected) with
per-card approve/reject/delete, infinite "load more", and a
PDF QR shortcut in the footer.
- Logout via Cloudflare Access /cdn-cgi/access/logout.
Shared (packages/shared/src/schemas.ts):
- New zod schemas for AdminUpload, AdminUploadsResponse, AdminStats
so the client and server agree on the admin payload shape.
https://claude.ai/code/session_01TPBqgcSJMppgrpiq7fLywL
Backend:
- POST /api/uploads/init validates against event_config (size, video
policy, duration), creates an uploads row, and returns either a
single presigned PUT or a multipart batch of presigned part URLs
depending on file size (50 MB threshold, 10 MB parts).
- POST /api/uploads/:id/confirm completes multipart on R2 if needed,
HEADs the object to verify upload, and flips status to approved
(post-moderation) or pending (pre-moderation).
- POST /api/uploads/:id/abort cancels in-flight multipart and removes
the row.
- GET /api/gallery returns approved uploads in cursor-paginated
reverse chronological order, with public R2 URLs.
- GET /api/stats returns lightweight counts for future home/admin use.
Frontend:
- lib/upload.ts handles single and multipart uploads via XHR with
progress callbacks, video duration extraction, and abort signals.
- /enviar: real form with file picker (image/video), author name,
message, per-file progress, multi-file support, and a success state.
- /galeria: responsive grid of approved items with lazy-loaded images,
video preview tiles, infinite "load more", and a fullscreen lightbox
showing the author and message.
Schema: turnstileToken is now optional so the MVP works without
Turnstile wired up; we layer it back in later.
https://claude.ai/code/session_01TPBqgcSJMppgrpiq7fLywL
Set up pnpm workspaces with three packages: a shared Zod-schema
package, a Hono-based API exported as a library, and a Vite/React/
Tailwind frontend that mounts the API via Cloudflare Pages Functions.
Storage is abstracted behind an S3-compatible provider so the project
can migrate from R2 to a self-hosted MinIO without code changes.
https://claude.ai/code/session_01TPBqgcSJMppgrpiq7fLywL