import crypto from "crypto"; // Simple in-memory CAPTCHA store (in production, use Redis or database) const captchaStore: Map = new Map(); const CAPTCHA_TTL = 5 * 60 * 1000; // 5 minutes const MAX_ATTEMPTS = 5; const CLEANUP_INTERVAL = 10 * 60 * 1000; // 10 minutes // Cleanup old captchas periodically setInterval(() => { const now = Date.now(); for (const [key, value] of captchaStore.entries()) { if (now - value.createdAt > CAPTCHA_TTL) { captchaStore.delete(key); } } }, CLEANUP_INTERVAL); export function generateCaptcha(identifier: string): { id: string; code: string } { const code = Math.random().toString().slice(2, 8); // 6-digit code const id = crypto.randomBytes(16).toString("hex"); captchaStore.set(id, { code, createdAt: Date.now(), attempts: 0, }); return { id, code }; } export interface CaptchaVerificationResult { success: boolean; error?: string; attemptsRemaining?: number; } export function verifyCaptcha(id: string, code: string): CaptchaVerificationResult { const captcha = captchaStore.get(id); if (!captcha) { return { success: false, error: "CAPTCHA not found or expired. Please reload and try again." }; } // Check if expired if (Date.now() - captcha.createdAt > CAPTCHA_TTL) { captchaStore.delete(id); return { success: false, error: "CAPTCHA code has expired. Please request a new one." }; } // Check attempts if (captcha.attempts >= MAX_ATTEMPTS) { captchaStore.delete(id); return { success: false, error: "Too many incorrect attempts. Please request a new CAPTCHA code." }; } // Verify code if (captcha.code === code) { captchaStore.delete(id); return { success: true }; } // Increment attempts captcha.attempts++; const remaining = MAX_ATTEMPTS - captcha.attempts; return { success: false, error: `Incorrect CAPTCHA code. ${remaining} attempt${remaining === 1 ? "" : "s"} remaining.`, attemptsRemaining: remaining, }; } export function getMaxAttempts(): number { return MAX_ATTEMPTS; }