Productieklare UGC-app voor nominaties van “medewerker van de maand”. Publieke feed + detailpagina’s, inzendformulier zonder account, moderatieflow, rapportages, anti-spam, SEO en juridische pagina’s.
- Next.js (App Router) + TypeScript
- TailwindCSS v4 + shadcn/ui
- Supabase (Postgres + Auth + Storage)
- Zod + React Hook Form
- sharp voor server-side image processing
- Install dependencies
npm install- Zet
.env.localop basis van.env.example(gebruik service role key alleen server-side):
SUPABASE_URL=...
SUPABASE_ANON_KEY=...
SUPABASE_SERVICE_ROLE_KEY=...
SUPABASE_JWT_SECRET=...
RATE_LIMIT_SALT=change-me
SITE_URL=http://localhost:3000
STORAGE_BUCKET=nominations
ADMIN_SEED_EMAILS=admin@example.com,moderator@example.com
- Migrations + seed uitvoeren (vereist Supabase CLI en een database URL):
supabase db push --db-url "$SUPABASE_DB_URL"
supabase db execute --file ./db/seed/seed.sql --db-url "$SUPABASE_DB_URL"- Dev server:
npm run dev- Publieke feed met zoek & maandfilter, detailpagina met signed image URLs.
- Nominatieformulier: foto-upload (jpg/png/webp), compressie/resize → webp, honeypot, rate-limit (3 per 24h per IP-hash), keyword blocklist.
- Moderatie: pending → approved/rejected, redactie, audit log, soft delete + storage cleanup.
- Rapportages (open/closed), verwijderverzoeken (open/closed).
- SEO: slug
/nominatie/{id}-{slug}, sitemap.xml met alleen approved, robots.txt. - Juridisch: /privacy, /richtlijnen, /verwijderverzoek.
- Tabellenschema:
db/migrations/001_init.sql(nominations, reports, audit_log, removal_requests, admins + RLS policies). - Bucket
nominations(private). Signed URLs voor alle foto’s; placeholders leven inpublic/placeholders. - RLS: publieke read alleen
status='approved' AND is_deleted=false; inserts voor anon op nominations/reports/removal_requests; updates/deletes alleen admin/mod viaadmins-tabel en JWT email claim. - Admin detectie: voeg e-mails toe aan
adminstabel (seedbestand bevat voorbeelden).
npm run dev– development servernpm run lint– lint met Next/ESLintnpm run test– Vitest smoke testsnpm run build– productie build
Vitest smoke tests:
tests/nominations.test.ts: mockt storage/Supabase en test create-nomination flow (slug + insert call).tests/feed.test.tsx: rendert feed page met mocked data.
- IP’s worden nooit raw opgeslagen, alleen een salted SHA-256 hash (
RATE_LIMIT_SALT). - Rate limit: max 3 inzendingen per 24 uur per IP-hash (DB + in-memory fallback).
- Honeypot veld
websiteop formulieren. - Keyword blocklist staat in
lib/nominations.ts. - Signed URLs voor beelden; bucket is private.
- Login via Supabase Auth (email/wachtwoord). Controle op e-mail in
admins-tabel. - Dashboard: pending/approved/rejected, statuswissels, redactie, soft delete (ruimt storage op), report-queue open/closed, verwijderverzoeken open/closed.
db/seed/seed.sqlbevat 5 approved, 2 pending, 1 rejected nominatie + een report en verwijderverzoek.- Placeholderbeelden:
public/placeholders/*.svg.
- Zet
SITE_URLvoor correcte sitemap/robots/share links. - Houd
SUPABASE_SERVICE_ROLE_KEYserver-side (env) en gebruik env-injectie bij deploy. - Verifieer dat bucket
nominationsbestaat (migration doetinsert ... on conflict do nothing).