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.
This commit is contained in:
@@ -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 += `<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();
|
||||
}
|
||||
Reference in New Issue
Block a user