devfest26-raffle
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v74View latest version
This val is a single HTTP app (main.http.ts) built with Hono. It serves both UI and API routes.
There are three UX surfaces:
- Public page (
/) for entry creation - Admin home (
/admin) for live operations (draw/reset) - Admin management (
/admin/manage) for prize pool CRUD and deeper inspection
id: stringstable identifiername: stringdisplay name
id: stringname: stringemail: stringcreatedAt: ISO stringprizeIds: string[]selected prize IDs
prizeId: stringprize: stringdenormalized nameentry: EntryannouncedAt: ISO string
Record<string, WinnerRecord>keyed byprizeId
Blob keys:
devfest26_raffle_prizes_v1devfest26_raffle_entries_v2devfest26_raffle_winners_v2
In-memory mirrors are maintained:
memoryPrizesmemoryEntriesmemoryWinners
Reason: local/dev resilience and graceful behavior if blob reads fail.
Normalization functions (normalizeEntry, normalizeWinners) handle older payload shapes:
- Legacy
prizeIndexesconverted to currentprizeIds - Missing prize selection defaults to "all current prizes"
- Winner records keyed/indexed by older formats are mapped to current
prizeId
- One entry per email
- Entrants may select one or more prizes
- One winner per prize
- Entrant can win at most one prize total
- Admin key required for all admin pages/APIs
- Fast event operations
- Winner draw controls
- Submissions overview
- Reset action
- Link to management view
- Add prize
- Rename prize
- Remove prize (two-click confirmation)
- Submissions + winners visibility
- Prize Pool section intentionally placed lower (less frequent action)
POST /api/entries
- Body:
{ name: string, email: string, prizeIds: string[] } - 201 on success, errors on validation/duplicates
All admin routes require ?key=<ADMIN_KEY>.
GET /api/admin/state
- Returns
{ entriesCount, winners, prizes }
POST /api/admin/draw
- Body:
{ prizeId: string } - Draws winner among eligible entrants for that prize
POST /api/admin/reset
- Clears entries + winners (prizes remain)
POST /api/admin/prizes
- Body:
{ name: string } - Adds prize
PATCH /api/admin/prizes/:prizeId
- Body:
{ name: string } - Renames prize
DELETE /api/admin/prizes/:prizeId
- Removes prize and prunes related entry/winner references
renderHome->GET /renderAdminHome->GET /adminrenderAdmin->GET /admin/managehandleCreateEntry->POST /api/entrieshandleDrawWinner->POST /api/admin/drawhandleResetRaffle->POST /api/admin/resethandleAddPrize->POST /api/admin/prizeshandleRenamePrize->PATCH /api/admin/prizes/:prizeIdhandleRemovePrize->DELETE /api/admin/prizes/:prizeId
- If UI seems stale, hard refresh browser.
- If admin pages return
401, key mismatch. - If a fetch tool URL encodes
?key=..., route may look like404even when real route works in browser.
