1 changed files with 44 additions and 0 deletions
@ -0,0 +1,44 @@ |
|||||
|
import svgCaptcha from 'svg-captcha' |
||||
|
|
||||
|
interface CaptchaRecord { |
||||
|
text: string |
||||
|
createdAt: number |
||||
|
} |
||||
|
|
||||
|
const captchaStore = new Map<string, CaptchaRecord>() |
||||
|
const CAPTCHA_TTL = 5 * 60 * 1000 |
||||
|
|
||||
|
function cleanupExpired(): void { |
||||
|
const now = Date.now() |
||||
|
for (const [token, record] of captchaStore) { |
||||
|
if (now - record.createdAt > CAPTCHA_TTL) { |
||||
|
captchaStore.delete(token) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function generateCaptcha(): { token: string; svg: string } { |
||||
|
cleanupExpired() |
||||
|
const { text, data: svg } = svgCaptcha.create({ |
||||
|
noise: 3, |
||||
|
color: true, |
||||
|
background: '#f8f9fa', |
||||
|
}) |
||||
|
const token = crypto.randomUUID() |
||||
|
captchaStore.set(token, { text, createdAt: Date.now() }) |
||||
|
return { token, svg } |
||||
|
} |
||||
|
|
||||
|
export function verifyCaptcha(token: string, text: string): boolean { |
||||
|
const record = captchaStore.get(token) |
||||
|
if (!record) return false |
||||
|
if (Date.now() - record.createdAt > CAPTCHA_TTL) { |
||||
|
captchaStore.delete(token) |
||||
|
return false |
||||
|
} |
||||
|
const match = record.text.toLowerCase() === text.toLowerCase() |
||||
|
if (match) { |
||||
|
captchaStore.delete(token) |
||||
|
} |
||||
|
return match |
||||
|
} |
||||
Loading…
Reference in new issue