Add contact email confirmation and abuse protection
This commit is contained in:
@@ -19,3 +19,7 @@ export const contactRequestSchema = z.object({
|
||||
});
|
||||
|
||||
export type ContactRequestInput = z.infer<typeof contactRequestSchema>;
|
||||
|
||||
export type ContactSubmissionPayload = ContactRequestInput & {
|
||||
website?: string;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { UserRole } from "@prisma/client";
|
||||
|
||||
type RedirectUser = {
|
||||
role?: UserRole | string;
|
||||
kitaId?: string | null;
|
||||
};
|
||||
|
||||
export function getPostLoginRedirect(user: RedirectUser) {
|
||||
if (user.role === UserRole.SUPERADMIN || user.role === "SUPERADMIN") {
|
||||
return "/admin";
|
||||
}
|
||||
|
||||
return user.kitaId ? "/dashboard" : "/onboarding";
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
type Bucket = {
|
||||
count: number;
|
||||
resetAt: number;
|
||||
};
|
||||
|
||||
const buckets = new Map<string, Bucket>();
|
||||
|
||||
export type RateLimitResult =
|
||||
| { allowed: true; remaining: number; resetAt: number }
|
||||
| { allowed: false; retryAfterSeconds: number };
|
||||
|
||||
export function rateLimit(
|
||||
key: string,
|
||||
limit: number,
|
||||
windowSeconds: number,
|
||||
): RateLimitResult {
|
||||
const now = Date.now();
|
||||
const windowMs = windowSeconds * 1000;
|
||||
const existing = buckets.get(key);
|
||||
|
||||
if (!existing || existing.resetAt <= now) {
|
||||
buckets.set(key, { count: 1, resetAt: now + windowMs });
|
||||
return { allowed: true, remaining: limit - 1, resetAt: now + windowMs };
|
||||
}
|
||||
|
||||
if (existing.count >= limit) {
|
||||
return {
|
||||
allowed: false,
|
||||
retryAfterSeconds: Math.ceil((existing.resetAt - now) / 1000),
|
||||
};
|
||||
}
|
||||
|
||||
existing.count += 1;
|
||||
return {
|
||||
allowed: true,
|
||||
remaining: limit - existing.count,
|
||||
resetAt: existing.resetAt,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user