Coloriginz Design Systemshadcn custom registry

@col/rate-limit

Versie: 1.0.0 Toegevoegd: 2026-08-06

Herkomst

Twee apps hadden elk de helft. KCB/src/lib/rate-limit.ts (16 regels) heeft een schuivend venster — correct, maar ruimt nooit op, dus de map groeit tot het proces herstart. supplier-onboarding-vercel/src/lib/rate-limit.ts (56 regels) is geparametriseerd (ip, key, max, windowMs) en ruimt bij elke aanroep op, maar gebruikt een vast venster: bij vijf per minuut kun je er vijf om 00:59 doen en nog eens vijf om 01:01, omdat de teller per minuutbucket reset in plaats van over de grens heen te kijken.

Dit item neemt KCB's schuivende venster en combineert dat met de parameters en de opruiming van supplier-onboarding. Zie docs/specs/2026-08-06-v10-basisitems.md, sectie 1, voor de volledige afweging.

De directe aanleiding was geen refactor-wens maar een gat: @col/auth-pages-starter levert drie POST-routes uit (activate, forgot-password, reset-password) die al een rateLimit() aanriepen, maar het bestand daarachter zat niet in het item. Dat compileerde in de bronapp (die had zelf zo'n bestand geschreven) maar niet in een nieuwe app die alleen de starter installeerde — het effect is dus geen stil onbeschermd endpoint, maar een falende build: de import lost niet op. Luidruchtiger dan stil, maar nog steeds een item dat bij een verse installatie niet draait. Zie docs/items/auth-pages-starter.md voor hoe dat item vanaf 1.1.0 @col/rate-limit als registryDependency gebruikt.

Waarvoor

Beperkt hoe vaak dezelfde sleutel — meestal ${ip}:<route> — binnen een tijdvenster mag aanroepen, met een in-memory Map, geen externe afhankelijkheid. Gebruik het vóór een onauthenticated POST-endpoint dat gevoelig is voor misbruik: wachtwoord vergeten/resetten, account activeren, een contactformulier, een externe webhook-ontvanger zonder eigen throttling.

Gebruik het niet als je een garantie nodig hebt die standhoudt over meerdere serverless-instanties heen, of na een process-restart — zie "Bekende beperkingen". Voor dat geval is dit niet het juiste startpunt.

Wat je app moet leveren

Niets — geen props, geen backend-contract, geen omgevingsvariabele. Eén functie, geïmporteerd en direct aangeroepen aan het begin van de route-handler:

import { rateLimit } from '@/lib/rate-limit'

const ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || 'unknown'
const { ok, remaining } = rateLimit(`${ip}:forgot-password`, 3, 15 * 60 * 1000)
if (!ok) {
  return NextResponse.json({ error: 'Too many requests' }, { status: 429 })
}
function rateLimit(
  key: string,
  max: number,
  windowMs: number,
): { ok: boolean; remaining: number }
Parameter Betekenis
key Volledig door de aanroeper samengesteld, bv. ${ip}:forgot-password. Twee aanroepen met dezelfde sleutel delen dezelfde teller; twee routes die per ongeluk dezelfde sleutel gebruiken, delen ook per ongeluk hun limiet — verzin een sleutel die zowel de identiteit (meestal IP) als de actie bevat.
max Maximum aantal geslaagde aanroepen binnen windowMs.
windowMs Venstergrootte in milliseconden.

Retourneert { ok: false, remaining: 0 } zodra de limiet is bereikt — een geblokkeerde aanroep telt zelf niet mee als extra aanroep, dus een aanhoudende stroom geblokkeerde requests laat de teller niet verder oplopen. ok: true geeft remaining het aantal nog resterende aanroepen in het huidige venster (nooit negatief).

Bestanden

Bestand Landt in Soort
rate-limit.ts lib/rate-limit.ts beheerd

Geen registryDependencies, geen cssVars. Eén bestand, geen dependencies.

Gebruikt door

App Sinds versie Opmerkingen
@col/auth-pages-starter 1.1.0 Als registryDependency — landt automatisch mee op lib/rate-limit.ts bij npx shadcn add @col/auth-pages-starter. Beveiligt de drie POST-routes (activate, forgot-password, reset-password) met sleutels van de vorm ${ip}:<route>.

Nog niet vanuit dit item geïnstalleerd: supplier-onboarding-vercel en KCB hebben elk hun eigen, oudere versie van dit bestand (dat is precies waar dit item uit is samengesteld — zie Herkomst) en zijn nog niet overgezet naar @col/rate-limit. Wie dat oppakt: voeg hier een regel toe.

Bewuste keuzes

Bekende beperkingen