Browse Source

feat(auth): svg captcha challenge factory

Made-with: Cursor
main
npmrun 8 hours ago
parent
commit
d898d997d7
  1. 13
      server/service/captcha/challenge.test.ts
  2. 18
      server/service/captcha/challenge.ts

13
server/service/captcha/challenge.test.ts

@ -0,0 +1,13 @@
import { describe, expect, test } from "bun:test";
import { createCaptchaChallenge } from "./challenge";
describe("createCaptchaChallenge", () => {
test("returns captchaId and non-empty svg", () => {
const { captchaId, imageSvg } = createCaptchaChallenge();
expect(captchaId).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
);
expect(imageSvg.length).toBeGreaterThan(50);
expect(imageSvg.includes("svg")).toBe(true);
});
});

18
server/service/captcha/challenge.ts

@ -0,0 +1,18 @@
import svgCaptcha from "svg-captcha";
import { captchaCreate } from "./store";
/** 易混淆字符已剔除,长度 5 */
const CHAR_PRESET = "abcdefghjkmnpqrstuvwxyz23456789";
export function createCaptchaChallenge(): { captchaId: string; imageSvg: string } {
const { data: imageSvg, text } = svgCaptcha.create({
size: 5,
noise: 2,
color: true,
charPreset: CHAR_PRESET,
background: "#f4f4f5",
});
const answerNormalized = text.toLowerCase();
const { captchaId } = captchaCreate(answerNormalized);
return { captchaId, imageSvg };
}
Loading…
Cancel
Save