This is a Next.js app that scores uploaded photos for “performativity” and features a public leaderboard. Users can opt in to publish their score, keywords, and an optional social handle. Images are stored in the DB as data URLs but served via cacheable routes, not embedded directly in pages.
- Node.js 18+ (20+ recommended)
- pnpm
- Env vars:
INFERENCE_API_KEY
,DATABASE_URL
pnpm install
Avoid multiple lockfiles. Use pnpm consistently.
Create .env
in project root:
INFERENCE_API_KEY=sk-...
DATABASE_URL=postgres://... (Neon)
pnpm dev
- UI:
app/page.tsx
handles upload, scoring, and submission with a leaderboard opt‑in (default true). - Scoring:
app/api/annotate/route.ts
calls Inference.net (inference-net/cliptagger-12b
) and returns structured JSON. - Leaderboard:
app/leaderboard/page.tsx
is a Server Component that streams a shell and renderscomponents/LeaderboardContent.tsx
with cached top entries (limit 25). - Image delivery: images are retrieved via
/img/[id]
→ redirects to/img/[id]/[hash]
with strong caching/ETag. - DB: Neon + Drizzle (
src/db.ts
,src/schema.ts
).
Scores a base64 data URL image using the upstream model.
Request
{ "imageDataUrl": "data:image/png;base64,AAAA..." }
Response (shape)
{ "success": true, "result": { "description": "...", "objects": ["..."], "summary": "..." }, "timings": { "totalMs": 0 } }
Saves a scored entry to the leaderboard (deduplicates by image hash or data URL).
Body
{
"imageDataUrl": "data:image/png;base64,AAAA...",
"socialPlatform": "twitter|instagram|tiktok",
"socialHandle": "handle",
"leaderboardOptIn": true
}
Response includes: id
, score
, matchedKeywords
, timestamps, and social fields.
Returns top entries (default limit 25), filterable.
Query params: minScore
, limit
, sort
, q
, maleOnly
(default true)
GET /img/[id]
→ 302 to/img/[id]/[hash]
and sets cache headers.GET /img/[id]/[hash]
→ serves bytes withETag
, long-lived immutable cache, and content type inferred from data URL.
Use these URLs in the UI, e.g. /img/123?w=256&fmt=webp&q=70
(query params are ignored by the server and can be used for client hints/CDN transforms).
- Only pnpm is used in this repo.
- The leaderboard Server Component caches only lightweight metadata; image bytes are fetched separately to avoid exceeding Next.js data cache limits.
MIT