diff --git a/src/lib/utils/rateLimiter.ts b/src/lib/utils/rateLimiter.ts new file mode 100644 index 0000000..1328b24 --- /dev/null +++ b/src/lib/utils/rateLimiter.ts @@ -0,0 +1,30 @@ +type RateLimitStore = { + [key: string]: { count: number; lastRequest: number }; +}; + +const rateLimitStore: RateLimitStore = {}; +const RATE_LIMIT_WINDOW = 60 * 5000; // 5 minutes +const MAX_REQUESTS = 5; + +export function isRateLimited(ip: string): boolean { + const currentTime = Date.now(); + const requestInfo = rateLimitStore[ip]; + + if (!requestInfo) { + rateLimitStore[ip] = { count: 1, lastRequest: currentTime }; + return false; + } + + if (currentTime - requestInfo.lastRequest > RATE_LIMIT_WINDOW) { + rateLimitStore[ip] = { count: 1, lastRequest: currentTime }; + return false; + } + + if (requestInfo.count >= MAX_REQUESTS) { + return true; + } + + rateLimitStore[ip].count += 1; + rateLimitStore[ip].lastRequest = currentTime; + return false; +} diff --git a/src/routes/contact/+page.server.ts b/src/routes/contact/+page.server.ts index 7073ebc..150a908 100644 --- a/src/routes/contact/+page.server.ts +++ b/src/routes/contact/+page.server.ts @@ -3,9 +3,17 @@ import { fail } from '@sveltejs/kit'; import { sendEmail } from '$lib/utils/brevo/email'; import { VITE_CONTACT_EMAIL } from '$env/static/private'; import logger from '$lib/utils/logger/logger'; +import { isRateLimited } from '$lib/utils/rateLimiter'; export const actions: Actions = { default: async ({ request }) => { + const ip = + request.headers.get('x-forwarded-for') ?? request.headers.get('remote-addr') ?? 'unknown'; + + if (isRateLimited(ip)) { + return fail(429, { error: 'Too many requests. Please try again later.' }); + } + const formData = await request.formData(); const name = formData.get('name') as string; const email = formData.get('email') as string;