3 changed files with 37 additions and 0 deletions
@ -0,0 +1,12 @@ |
|||||
|
import { getRequestIP } from "h3"; |
||||
|
import { createCaptchaChallenge } from "#server/service/captcha/challenge"; |
||||
|
import { assertUnderRateLimit } from "#server/utils/simple-rate-limit"; |
||||
|
|
||||
|
export default defineWrappedResponseHandler(async (event) => { |
||||
|
setResponseHeader(event, "cache-control", "no-store"); |
||||
|
const ip = getRequestIP(event, { xForwardedFor: true }) ?? "unknown"; |
||||
|
assertUnderRateLimit(`auth-captcha:${ip}`, 60, 60_000); |
||||
|
|
||||
|
const { captchaId, imageSvg } = createCaptchaChallenge(); |
||||
|
return R.success({ captchaId, imageSvg }); |
||||
|
}); |
||||
@ -0,0 +1,24 @@ |
|||||
|
export type AuthCredentialsAndCaptcha = { |
||||
|
username: string; |
||||
|
password: string; |
||||
|
captchaId: string; |
||||
|
captchaAnswer: string; |
||||
|
}; |
||||
|
|
||||
|
export function assertLoginRegisterCaptchaFieldsPresent( |
||||
|
body: unknown, |
||||
|
): asserts body is AuthCredentialsAndCaptcha { |
||||
|
if (typeof body !== "object" || body === null) { |
||||
|
throw createError({ statusCode: 400, statusMessage: "无效请求" }); |
||||
|
} |
||||
|
const b = body as Record<string, unknown>; |
||||
|
if (typeof b.username !== "string" || typeof b.password !== "string") { |
||||
|
throw createError({ statusCode: 400, statusMessage: "无效请求" }); |
||||
|
} |
||||
|
if (typeof b.captchaId !== "string" || b.captchaId.trim() === "") { |
||||
|
throw createError({ statusCode: 400, statusMessage: "请完成验证码" }); |
||||
|
} |
||||
|
if (typeof b.captchaAnswer !== "string" || b.captchaAnswer.trim() === "") { |
||||
|
throw createError({ statusCode: 400, statusMessage: "请完成验证码" }); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue