49 lines
1.1 KiB
TypeScript
49 lines
1.1 KiB
TypeScript
import { randomBytes } from "node:crypto";
|
|
import { z } from "astro/zod";
|
|
import { defineAction } from "astro:actions";
|
|
import { and, db, eq, gte, Otp, SendmailToken } from "astro:db";
|
|
|
|
export default defineAction({
|
|
input: z.object({
|
|
guess: z.string().length(6),
|
|
lenient: z.boolean().default(false),
|
|
userId: z.string().nonempty(),
|
|
}),
|
|
handler: verifyOtp,
|
|
});
|
|
|
|
async function verifyOtp({ guess, lenient, userId }: VerifyOtpParams) {
|
|
const leniency = lenient ? 1000 * 60 : 0;
|
|
const isOtpCorrect =
|
|
(await db.$count(
|
|
Otp,
|
|
and(
|
|
eq(Otp.userId, userId),
|
|
eq(Otp.value, guess),
|
|
gte(Otp.validUntil, Date.now() - leniency),
|
|
),
|
|
)) > 0;
|
|
|
|
if (!isOtpCorrect) {
|
|
return false;
|
|
}
|
|
|
|
await db.delete(Otp).where(and(eq(Otp.userId, userId), eq(Otp.value, guess)));
|
|
|
|
const token = randomBytes(256).toString("hex");
|
|
await db.insert(SendmailToken).values({
|
|
userId,
|
|
value: token,
|
|
createdAt: Date.now(),
|
|
validUntil: Date.now() + 60_000,
|
|
});
|
|
|
|
return token;
|
|
}
|
|
|
|
type VerifyOtpParams = {
|
|
guess: string;
|
|
lenient: boolean;
|
|
userId: string;
|
|
};
|