Files
teknav-bak/app/backend/src/lib/captcha.ts
T
arsam 043a97556f 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.
2026-09-18 23:19:13 +03:30

72 lines
2.4 KiB
TypeScript

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 += `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${color}" stroke-width="1.5" opacity="0.3"/>`;
}
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 += `<text x="${x}" y="${y}" font-size="${fontSize}" font-weight="bold" fill="${color}" transform="rotate(${rotation} ${x} ${y})" font-family="Arial, sans-serif">${char}</text>`;
}
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
<rect width="${width}" height="${height}" fill="${bgColor}"/>
${noiseSVG}
${textSVG}
</svg>`;
}
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<boolean> {
const key = CAPTCHA_PREFIX + captchaId;
const stored = await redis.get(key);
if (!stored) return false;
await redis.del(key);
return stored === userSolution.toLowerCase().trim();
}