All files / apps/web/src/handlers/user resend-email.ts

100% Statements 12/12
100% Branches 4/4
100% Functions 1/1
100% Lines 12/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53          6x             5x 5x         5x 1x         4x     4x           1x         3x 3x 1x               1x        
import { TRPCError } from "@trpc/server";
 
import { sendVerificationEmail } from "~web/handlers/auth/utils";
import { authProcedure } from "~web/handlers/trpc";
 
export const procedure = authProcedure
	.meta({
		title: "Resend verification email",
		description:
			"Resends the  user verification email, rate-limited to once per hour.",
	})
	.mutation(async ({ ctx }) => {
		const { database } = ctx;
		const user = await database
			.selectFrom("users")
			.select(["email", "confirmationTokenTimestamp"])
			.where("id", "=", ctx.auth.userId)
			.executeTakeFirstOrThrow();
		if (!user.confirmationTokenTimestamp) {
			throw new TRPCError({
				code: "BAD_REQUEST",
				message: `User "${ctx.auth.email}" is already verified.`,
			});
		}
		const retryTimestamp = user.confirmationTokenTimestamp.add({
			hours: 1,
		});
		if (
			Temporal.ZonedDateTime.compare(
				Temporal.Now.zonedDateTimeISO(),
				retryTimestamp,
			) < 0
		) {
			throw new TRPCError({
				code: "BAD_REQUEST",
				message: `Verification email to "${ctx.auth.email}" was sent less than an hour ago. Please try again later.`,
			});
		}
		const token = ctx.getUuid();
		await sendVerificationEmail(ctx, user.email, token);
		await database
			.updateTable("users")
			.set({
				confirmationToken: token,
				confirmationTokenTimestamp: Temporal.Now.zonedDateTimeISO(),
			})
			.where("id", "=", ctx.auth.userId)
			.execute();
		return {
			email: user.email,
		};
	});