All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m18s
54 lines
2.0 KiB
TypeScript
54 lines
2.0 KiB
TypeScript
import u from "@/utils";
|
|
import { hashPassword, verifyPassword } from "@/lib/password";
|
|
|
|
export type SmsPurpose = "register" | "resetPassword";
|
|
|
|
const EXPIRES_IN_MS = 5 * 60 * 1000;
|
|
const RESEND_INTERVAL_MS = 60 * 1000;
|
|
const MAX_ATTEMPTS = 5;
|
|
|
|
function createSmsCodeError(message: string) {
|
|
const error = new Error(message) as Error & { status: number };
|
|
error.status = 400;
|
|
return error;
|
|
}
|
|
|
|
export function createNumericCode() {
|
|
return String(Math.floor(100000 + Math.random() * 900000));
|
|
}
|
|
|
|
export async function assertCanSendSmsCode(phone: string, purpose: SmsPurpose) {
|
|
const latest = await u.db("o_smsCode").where({ phone, purpose, used: 0 }).orderBy("createTime", "desc").first();
|
|
if (latest?.sentAt && Date.now() - latest.sentAt < RESEND_INTERVAL_MS) {
|
|
const waitSeconds = Math.ceil((RESEND_INTERVAL_MS - (Date.now() - latest.sentAt)) / 1000);
|
|
throw createSmsCodeError(`验证码发送过于频繁,请 ${waitSeconds} 秒后再试`);
|
|
}
|
|
}
|
|
|
|
export async function saveSmsCode(phone: string, purpose: SmsPurpose, code: string) {
|
|
await u.db("o_smsCode").insert({
|
|
id: Date.now(),
|
|
phone,
|
|
purpose,
|
|
codeHash: await hashPassword(code),
|
|
expiresAt: Date.now() + EXPIRES_IN_MS,
|
|
used: 0,
|
|
attempts: 0,
|
|
createTime: Date.now(),
|
|
sentAt: Date.now(),
|
|
});
|
|
}
|
|
|
|
export async function verifySmsCode(phone: string, purpose: SmsPurpose, code: string) {
|
|
const record = await u.db("o_smsCode").where({ phone, purpose, used: 0 }).orderBy("createTime", "desc").first();
|
|
if (!record) throw createSmsCodeError("验证码不存在或已失效");
|
|
if ((record.attempts ?? 0) >= MAX_ATTEMPTS) throw createSmsCodeError("验证码尝试次数过多,请重新获取");
|
|
if ((record.expiresAt ?? 0) < Date.now()) throw createSmsCodeError("验证码已过期");
|
|
|
|
await u.db("o_smsCode").where("id", record.id).update({ attempts: (record.attempts ?? 0) + 1 });
|
|
const matched = await verifyPassword(code, record.codeHash);
|
|
if (!matched) throw createSmsCodeError("验证码错误");
|
|
|
|
await u.db("o_smsCode").where("id", record.id).update({ used: 1 });
|
|
}
|