Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ export function CustomerPageClient() {
const { customerId } = useParams<{ customerId: string }>();

const { id: workspaceId, slug } = useWorkspace();
const {
data: customer,
isLoading,
error,
} = useCustomer<CustomerEnriched>({
const { data: customer, isLoading } = useCustomer<CustomerEnriched>({
customerId,
query: { includeExpandedFields: true },
});
Expand All @@ -41,7 +37,7 @@ export function CustomerPageClient() {
fetcher,
);

if (!customer && !isLoading && !error) notFound();
if (!customer && !isLoading) notFound();

return (
<div className="mb-10 mt-2">
Expand Down
36 changes: 29 additions & 7 deletions apps/web/lib/actions/create-user-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import { ratelimit } from "@/lib/upstash";
import { prisma } from "@dub/prisma";
import { waitUntil } from "@vercel/functions";
import { flattenValidationErrors } from "next-safe-action";
import { createId } from "../api/create-id";
import { getIP } from "../api/utils";
import { hashPassword } from "../auth/password";
import z from "../zod";
import { signUpSchema } from "../zod/schemas/auth";
Expand All @@ -15,6 +15,9 @@ const schema = signUpSchema.extend({
code: z.string().min(6, "OTP must be 6 characters long."),
});

const MAX_OTP_ATTEMPTS = 5; // Block after 5 failed attempts
const OTP_LOCKOUT_DURATION = "24 h"; // Block for 24 hours

// Sign up a new user using email and password
export const createUserAccountAction = actionClient
.schema(schema, {
Expand All @@ -25,26 +28,45 @@ export const createUserAccountAction = actionClient
.action(async ({ parsedInput }) => {
const { email, password, code } = parsedInput;

const { success } = await ratelimit(2, "1 m").limit(`signup:${getIP()}`);
const signupAttemptKey = `signup:attempts:${email}`;

const { remaining: attemptsRemaining } = await ratelimit(
MAX_OTP_ATTEMPTS,
OTP_LOCKOUT_DURATION,
).getRemaining(signupAttemptKey);

if (!success) {
throw new Error("Too many requests. Please try again later.");
if (attemptsRemaining <= 0) {
throw new Error("Too many failed attempts. You have to try again later.");
}

const verificationToken = await prisma.emailVerificationToken.findUnique({
where: {
identifier: email,
token: code,
expires: {
gte: new Date(),
},
},
});

if (!verificationToken) {
await ratelimit(MAX_OTP_ATTEMPTS, OTP_LOCKOUT_DURATION).limit(
signupAttemptKey,
);

throw new Error("Invalid verification code entered.");
}

if (verificationToken.expires && verificationToken.expires < new Date()) {
waitUntil(
prisma.emailVerificationToken.delete({
where: {
identifier: email,
token: code,
},
}),
);

throw new Error("The OTP has expired. Please request a new one.");
}

await prisma.emailVerificationToken.delete({
where: {
identifier: email,
Expand Down
7 changes: 6 additions & 1 deletion apps/web/lib/zod/schemas/leads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ export const trackLeadRequestSchema = z.object({
.record(z.unknown())
.nullish()
.default(null)
.describe("Additional metadata to be stored with the lead event"),
.refine((val) => !val || JSON.stringify(val).length <= 10000, {
message: "Metadata must be less than 10,000 characters when stringified",
})
.describe(
"Additional metadata to be stored with the lead event. Max 10,000 characters.",
),
mode: z
.enum(["async", "wait"])
.default("async")
Expand Down
7 changes: 6 additions & 1 deletion apps/web/lib/zod/schemas/sales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ export const trackSaleRequestSchema = z.object({
.record(z.unknown())
.nullish()
.default(null)
.describe("Additional metadata to be stored with the sale event."),
.refine((val) => !val || JSON.stringify(val).length <= 10000, {
message: "Metadata must be less than 10,000 characters when stringified",
})
.describe(
"Additional metadata to be stored with the sale event. Max 10,000 characters.",
),
leadEventName: z
.string()
.nullish()
Expand Down