feat(www): rate limit cio subscribe form routes

This commit is contained in:
Pamela Chia committed 2026-07-31 16:52:56 +08:00
1 parent a9a2832237
commit 513d9ea459
3 files changed
+49 -2

No files matched your search

@@ -1,6 +1,6 @@
import * as Sentry from '@sentry/nextjs'
import { CustomerioTrackClient } from '~/lib/customerio'
import { createRateLimiter } from '~/lib/rate-limit'
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
@@ -12,7 +12,16 @@ const isValidEmail = (email: string): boolean => {
return emailPattern.test(email)
}
const isRateLimited = createRateLimiter({ max: 5, windowMs: 60 * 1000 })
export async function POST(req: Request) {
if (isRateLimited(req)) {
return new Response(JSON.stringify({ message: 'Too many requests. Try again later.' }), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 429,
})
}
const body = await req.json()
const { firstName, lastName, email } = body
@@ -0,0 +1,29 @@
import { describe, expect, it, vi } from 'vitest'
import { POST } from './route'
vi.mock('server-only', () => ({}))
vi.mock('@sentry/nextjs', () => ({ captureException: vi.fn() }))
const makeRequest = (ip: string) =>
new Request('http://localhost/api-v2/submit-form-subprocessor-updates', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-forwarded-for': ip },
body: JSON.stringify({}),
})
describe('submit-form-subprocessor-updates rate limiting', () => {
it('returns 429 on the sixth request in a window from one ip', async () => {
for (let i = 0; i < 5; i++) {
const res = await POST(makeRequest('203.0.113.7'))
expect(res.status).toBe(422)
}
const res = await POST(makeRequest('203.0.113.7'))
expect(res.status).toBe(429)
})
it('does not rate limit a different ip', async () => {
const res = await POST(makeRequest('203.0.113.8'))
expect(res.status).toBe(422)
})
})
@@ -1,6 +1,6 @@
import * as Sentry from '@sentry/nextjs'
import { CustomerioTrackClient } from '~/lib/customerio'
import { createRateLimiter } from '~/lib/rate-limit'
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
@@ -12,7 +12,16 @@ const isValidEmail = (email: string): boolean => {
return emailPattern.test(email)
}
const isRateLimited = createRateLimiter({ max: 5, windowMs: 60 * 1000 })
export async function POST(req: Request) {
if (isRateLimited(req)) {
return new Response(JSON.stringify({ message: 'Too many requests. Try again later.' }), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 429,
})
}
const body = await req.json()
const { firstName, lastName, email } = body