Problem
PR #217 added view count tracking with session-based dedup (1 per hour per session per page). However, there is no server-side rate limiting — a malicious actor can inflate view counts by scripting POST requests to /api/views with random sessionId values.
Fix
Add per-IP rate limiting to the POST /api/views endpoint. Suggested: max 10 view increments per storyline per IP per hour.
Options:
- Supabase RLS insert policy with rate check against
page_views table
- In-memory rate limiter in the API route (e.g., Map with IP + storylineId key, TTL 1 hour)
- Supabase function that checks recent inserts from the same IP before allowing
Also consider storing the viewer's IP in page_views for audit purposes (hash it for privacy).
Files
src/app/api/views/route.ts — add rate limiting logic
Acceptance Criteria
Problem
PR #217 added view count tracking with session-based dedup (1 per hour per session per page). However, there is no server-side rate limiting — a malicious actor can inflate view counts by scripting POST requests to
/api/viewswith randomsessionIdvalues.Fix
Add per-IP rate limiting to the POST
/api/viewsendpoint. Suggested: max 10 view increments per storyline per IP per hour.Options:
page_viewstableAlso consider storing the viewer's IP in
page_viewsfor audit purposes (hash it for privacy).Files
src/app/api/views/route.ts— add rate limiting logicAcceptance Criteria
/api/viewsrejects excessive requests from the same IP