import { randomBytes } from 'node:crypto'; import { redis } from '../redis.js'; const CAPTCHA_PREFIX = 'captcha:'; const CAPTCHA_TTL = 120; function generateCaptchaText(length: number = 5): string { const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } function generateCaptchaSVG(text: string): string { const width = 200; const height = 80; const colors = ['#0F6B73', '#D49A2A', '#C76D4A', '#2F8F6B', '#5F6B6D']; const bgColor = '#f0f0f0'; let noiseSVG = ''; for (let i = 0; i < 5; i++) { const x1 = Math.random() * width; const y1 = Math.random() * height; const x2 = Math.random() * width; const y2 = Math.random() * height; const color = colors[Math.floor(Math.random() * colors.length)]; noiseSVG += ``; } let textSVG = ''; const charSpacing = width / (text.length + 1); for (let i = 0; i < text.length; i++) { const char = text[i]; const x = charSpacing * (i + 1) + (Math.random() - 0.5) * 10; const y = height / 2 + (Math.random() - 0.5) * 15; const rotation = (Math.random() - 0.5) * 30; const color = colors[Math.floor(Math.random() * colors.length)]; const fontSize = 32 + Math.random() * 8; textSVG += `${char}`; } return ` ${noiseSVG} ${textSVG} `; } export async function createCaptcha() { const id = randomBytes(16).toString('hex'); const text = generateCaptchaText(5); const svg = generateCaptchaSVG(text); await redis.set(CAPTCHA_PREFIX + id, text.toLowerCase(), 'EX', CAPTCHA_TTL); return { id, data: svg }; } export async function verifyCaptcha(captchaId: string, userSolution: string): Promise { const key = CAPTCHA_PREFIX + captchaId; const stored = await redis.get(key); if (!stored) return false; await redis.del(key); return stored === userSolution.toLowerCase().trim(); }