feat: add client-side rate limiting to contact form
- Added `useRateLimit` hook - Integrated hook into `Contact.tsx` - Added translations for rate limit error - Added unit tests - Fixed type error in `Button.tsx` to allow build to pass
This commit is contained in:
38
src/hooks/useRateLimit.ts
Normal file
38
src/hooks/useRateLimit.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
interface UseRateLimitReturn {
|
||||
checkRateLimit: () => boolean;
|
||||
remainingTime: number;
|
||||
}
|
||||
|
||||
export function useRateLimit(key: string, cooldownMs: number): UseRateLimitReturn {
|
||||
const [remainingTime, setRemainingTime] = useState(0);
|
||||
|
||||
const checkRateLimit = useCallback(() => {
|
||||
try {
|
||||
const now = Date.now();
|
||||
const lastAttempt = localStorage.getItem(key);
|
||||
|
||||
if (lastAttempt) {
|
||||
const lastTime = parseInt(lastAttempt, 10);
|
||||
const timePassed = now - lastTime;
|
||||
|
||||
if (timePassed < cooldownMs) {
|
||||
const remaining = Math.ceil((cooldownMs - timePassed) / 1000);
|
||||
setRemainingTime(remaining);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem(key, now.toString());
|
||||
setRemainingTime(0);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.warn('LocalStorage not available:', error);
|
||||
// Fail safe: allow action if storage fails
|
||||
return true;
|
||||
}
|
||||
}, [key, cooldownMs]);
|
||||
|
||||
return { checkRateLimit, remainingTime };
|
||||
}
|
||||
Reference in New Issue
Block a user