You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
66 lines
2.1 KiB
66 lines
2.1 KiB
import { randomBytes, createHash } from "node:crypto";
|
|
|
|
function randomInt(min: number, max: number): number {
|
|
const range = max - min + 1;
|
|
const maxRand = 256 - (256 % range);
|
|
let rand: number;
|
|
do {
|
|
rand = randomBytes(1)[0];
|
|
} while (rand >= maxRand);
|
|
return min + (rand % range);
|
|
}
|
|
|
|
function randomChar(charset: string): string {
|
|
return charset[randomInt(0, charset.length - 1)];
|
|
}
|
|
|
|
export function generateCaptchaCode(length: number = 4): string {
|
|
const charset = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
let code = "";
|
|
for (let i = 0; i < length; i++) {
|
|
code += randomChar(charset);
|
|
}
|
|
return code;
|
|
}
|
|
|
|
export function generateCaptchaToken(): string {
|
|
return randomBytes(32).toString("hex");
|
|
}
|
|
|
|
export function generateCaptchaSvg(code: string): string {
|
|
const width = 120;
|
|
const height = 44;
|
|
const charWidth = width / code.length;
|
|
const colors = ["#0066cc", "#1d1d1f", "#0071e3", "#333333"];
|
|
|
|
let paths = "";
|
|
let texts = "";
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
const x1 = randomInt(0, width);
|
|
const y1 = randomInt(0, height);
|
|
const x2 = randomInt(0, width);
|
|
const y2 = randomInt(0, height);
|
|
paths += `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#e0e0e0" stroke-width="1" />`;
|
|
}
|
|
|
|
for (let i = 0; i < code.length; i++) {
|
|
const x = charWidth * i + randomInt(4, charWidth - 20);
|
|
const y = randomInt(26, 34);
|
|
const rotate = randomInt(-20, 20);
|
|
const color = colors[randomInt(0, colors.length - 1)];
|
|
const fontSize = randomInt(22, 28);
|
|
texts += `<text x="${x}" y="${y}" font-size="${fontSize}" fill="${color}" transform="rotate(${rotate}, ${x}, ${y})" font-family="Arial, sans-serif" font-weight="bold">${code[i]}</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="#fafafc" rx="8" />
|
|
${paths}
|
|
${texts}
|
|
</svg>`;
|
|
}
|
|
|
|
export function captchaSvgToDataUri(svg: string): string {
|
|
const base64 = Buffer.from(svg).toString("base64");
|
|
return `data:image/svg+xml;base64,${base64}`;
|
|
}
|
|
|