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:
@@ -2,6 +2,7 @@ import { useState, type FormEvent } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import emailjs from "@emailjs/browser";
|
||||
import { useTranslation } from "../i18n";
|
||||
import { useRateLimit } from "../hooks";
|
||||
import { config } from "../config";
|
||||
import { Button, Input, Textarea } from "../components/ui";
|
||||
import { sanitizeInput } from "../utils/security";
|
||||
@@ -38,6 +39,8 @@ export function Contact() {
|
||||
const [submitStatus, setSubmitStatus] = useState<
|
||||
"idle" | "success" | "error"
|
||||
>("idle");
|
||||
const [rateLimitError, setRateLimitError] = useState(false);
|
||||
const { checkRateLimit } = useRateLimit("contact-form", 60000); // 1 minute cooldown
|
||||
|
||||
const validateForm = (): boolean => {
|
||||
const newErrors: FormErrors = {};
|
||||
@@ -73,8 +76,14 @@ export function Contact() {
|
||||
const handleSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
setRateLimitError(false);
|
||||
if (!validateForm()) return;
|
||||
|
||||
if (!checkRateLimit()) {
|
||||
setRateLimitError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
setSubmitStatus("idle");
|
||||
|
||||
@@ -215,6 +224,16 @@ export function Contact() {
|
||||
{t.contact.form.error}
|
||||
</motion.p>
|
||||
)}
|
||||
|
||||
{rateLimitError && (
|
||||
<motion.p
|
||||
className={styles.error}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
>
|
||||
{t.contact.form.rateLimit}
|
||||
</motion.p>
|
||||
)}
|
||||
</form>
|
||||
</motion.div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user