From d1d40c297efc325115b8c6b04b89a5097da838b7 Mon Sep 17 00:00:00 2001 From: Adrian Bonpin Date: Sun, 26 Apr 2026 08:00:45 -0500 Subject: [PATCH] feat: add check-email API endpoint for login email verification --- app/api/auth/check-email/route.ts | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 app/api/auth/check-email/route.ts diff --git a/app/api/auth/check-email/route.ts b/app/api/auth/check-email/route.ts new file mode 100644 index 0000000..a7932f5 --- /dev/null +++ b/app/api/auth/check-email/route.ts @@ -0,0 +1,66 @@ +import { NextResponse } from "next/server" +import { db } from "@/lib/db" +import { user } from "@/lib/db/schema/auth" +import { eq } from "drizzle-orm" +import { loginEmailSchema } from "@/lib/auth/validation" + +// Simple in-memory rate limiter for this endpoint +const rateLimitStore = new Map() + +function checkRateLimit(ip: string): boolean { + const now = Date.now() + const window = 60_000 // 1 minute + const max = 10 // 10 requests per minute per IP + + const entry = rateLimitStore.get(ip) + if (!entry || now > entry.resetAt) { + rateLimitStore.set(ip, { count: 1, resetAt: now + window }) + return true + } + if (entry.count >= max) { + return false + } + entry.count++ + return true +} + +export async function POST(request: Request) { + // Rate limit by IP + const forwarded = request.headers.get("x-forwarded-for") + const ip = forwarded?.split(",")[0]?.trim() || "unknown" + + if (!checkRateLimit(ip)) { + return NextResponse.json( + { error: "Too many requests. Please try again later." }, + { status: 429 }, + ) + } + + let body: unknown + try { + body = await request.json() + } catch { + return NextResponse.json( + { error: "Invalid request body" }, + { status: 400 }, + ) + } + + const result = loginEmailSchema.safeParse(body) + if (!result.success) { + return NextResponse.json( + { error: result.error.issues[0].message }, + { status: 400 }, + ) + } + + const { email } = result.data + + const existingUser = await db + .select({ id: user.id }) + .from(user) + .where(eq(user.email, email.toLowerCase())) + .limit(1) + + return NextResponse.json({ exists: existingUser.length > 0 }) +} \ No newline at end of file