From 043a97556f498bf6c54c725ed10c0172f8283d6b Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:19:13 +0330 Subject: [PATCH] Import source: app/backend/src/lib/captcha Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- app/backend/src/lib/captcha.ts | 71 ++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 app/backend/src/lib/captcha.ts diff --git a/app/backend/src/lib/captcha.ts b/app/backend/src/lib/captcha.ts new file mode 100644 index 0000000..6c252c5 --- /dev/null +++ b/app/backend/src/lib/captcha.ts @@ -0,0 +1,71 @@ +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(); +}