Files
biblical-guide.com/lib/captcha.ts
Andrei 989f231d5a feat: add captcha verification to contact form
Added math-based captcha system to prevent spam on the contact form:
- Created captcha API endpoint with simple arithmetic questions
- Added captcha UI component with refresh functionality
- Integrated captcha verification into contact form submission
- Relaxed spam filters since captcha provides better protection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 17:53:56 +00:00

94 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { randomInt } from 'crypto'
interface CaptchaChallenge {
answer: number
expires: number
}
// Simple in-memory store for captcha challenges
const captchaStore = new Map<string, CaptchaChallenge>()
// Clean up expired captchas every 5 minutes
setInterval(() => {
const now = Date.now()
for (const [key, value] of captchaStore.entries()) {
if (value.expires < now) {
captchaStore.delete(key)
}
}
}, 5 * 60 * 1000)
function generateCaptchaId(): string {
return `captcha_${Date.now()}_${randomInt(10000, 99999)}`
}
export interface CaptchaData {
captchaId: string
question: string
}
export function generateCaptcha(): CaptchaData {
// Generate simple math problem
const num1 = randomInt(1, 20)
const num2 = randomInt(1, 20)
const operations = ['+', '-', '×'] as const
const operation = operations[randomInt(0, operations.length)]
let answer: number
let question: string
switch (operation) {
case '+':
answer = num1 + num2
question = `${num1} + ${num2}`
break
case '-':
// Ensure positive result
const larger = Math.max(num1, num2)
const smaller = Math.min(num1, num2)
answer = larger - smaller
question = `${larger} - ${smaller}`
break
case '×':
// Use smaller numbers for multiplication
const small1 = randomInt(2, 10)
const small2 = randomInt(2, 10)
answer = small1 * small2
question = `${small1} × ${small2}`
break
}
const captchaId = generateCaptchaId()
// Store captcha with 10 minute expiration
captchaStore.set(captchaId, {
answer,
expires: Date.now() + 10 * 60 * 1000
})
return {
captchaId,
question
}
}
export function verifyCaptcha(captchaId: string, answer: string | number): boolean {
const stored = captchaStore.get(captchaId)
if (!stored) {
return false
}
if (stored.expires < Date.now()) {
captchaStore.delete(captchaId)
return false
}
const isValid = parseInt(answer.toString()) === stored.answer
// Delete captcha after verification (one-time use)
captchaStore.delete(captchaId)
return isValid
}