feat: ST-601-ST-612 M6 polish, beveiliging en launch-ready
- ST-601/602: loading skeletons en error boundary - ST-603: Sonner toasts op alle CRUD-operaties - ST-604: DemoTooltip op uitgeschakelde knoppen - ST-605: KeyboardSensor dnd-kit, Escape sluit modals - ST-606: min-width banner < 1024px - ST-607: WCAG AA aria-labels en skip link - ST-608: rate limiting login (10/min) en registratie (5/uur) - ST-609: security integratietests cross-user toegang (7 tests) - ST-610: GitHub Actions CI/CD workflow - ST-611: README met quickstart, deployment en API-docs - ST-612: Lars-flow acceptatiechecklist - fix: settings toont gebruikersnaam i.p.v. interne id - fix: seed idempotent, testdata altijd gekoppeld aan demo-gebruiker Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8bb8754d01
commit
d11b114fc1
27 changed files with 1858 additions and 67 deletions
36
lib/rate-limit.ts
Normal file
36
lib/rate-limit.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Simple in-memory rate limiter.
|
||||
// Note: resets on server restart and does not share state across multiple processes.
|
||||
// Suitable for MVP; replace with Redis for production scale-out.
|
||||
|
||||
interface RateLimitConfig {
|
||||
windowMs: number
|
||||
max: number
|
||||
}
|
||||
|
||||
const CONFIGS: Record<string, RateLimitConfig> = {
|
||||
login: { windowMs: 60_000, max: 10 }, // 10 attempts per minute
|
||||
register: { windowMs: 3_600_000, max: 5 }, // 5 attempts per hour
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: RateLimitConfig = { windowMs: 60_000, max: 10 }
|
||||
|
||||
const store = new Map<string, { count: number; resetAt: number }>()
|
||||
|
||||
export function checkRateLimit(key: string): boolean {
|
||||
const prefix = key.split(':')[0]
|
||||
const config = CONFIGS[prefix] ?? DEFAULT_CONFIG
|
||||
const now = Date.now()
|
||||
const entry = store.get(key)
|
||||
|
||||
if (!entry || now > entry.resetAt) {
|
||||
store.set(key, { count: 1, resetAt: now + config.windowMs })
|
||||
return true
|
||||
}
|
||||
|
||||
if (entry.count >= config.max) {
|
||||
return false
|
||||
}
|
||||
|
||||
entry.count++
|
||||
return true
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue