import { NextRequest, NextResponse } from 'next/server'; import { withDb } from '@/lib/api-handler'; import { db } from '@/lib/db'; import { z } from 'zod'; import { getClientIp } from '@/lib/validation'; import { rateLimit } from '@/lib/rate-limit'; const forgotPasswordSchema = z.object({ email: z.string().email('Please enter a valid email address'), }); async function handler(request: NextRequest) { try { // ── Rate Limit: 3 per 15 minutes ───────────────────────────────── const ip = getClientIp(request); const { allowed, retryAfterMs } = await rateLimit(`forgot-pw:${ip}`, 3, 15 * 60_000); if (!allowed) { return NextResponse.json( { error: 'Too many reset attempts. Please try again later.' }, { status: 429, headers: { 'Retry-After': String(Math.ceil(retryAfterMs / 1000)) }, } ); } // ── Parse & Validate Body ───────────────────────────────────────── const body = await request.json(); const parsed = forgotPasswordSchema.safeParse(body); if (!parsed.success) { return NextResponse.json( { error: parsed.error.issues[0].message }, { status: 400 } ); } const { email } = parsed.data; // ── Check if user exists ────────────────────────────────────────── const user = await db.user.findUnique({ where: { email } }); if (user) { // Generate a random 6-digit code const code = String(Math.floor(100000 + Math.random() * 900000)); const expiresAt = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes // Invalidate any existing tokens for this email await db.resetToken.updateMany({ where: { email, usedAt: null }, data: { usedAt: new Date() }, }); // Create new reset token await db.resetToken.create({ data: { email, token: code, expiresAt, }, }); return NextResponse.json({ message: 'If an account exists with this email, a reset code has been generated.', code, // Demo: return code so user can use it }); } // Return same message regardless to prevent email enumeration return NextResponse.json({ message: 'If an account exists with this email, a reset code has been generated.', }); } catch (error) { console.error('[FORGOT_PASSWORD_ERROR]', error); return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ); } } export const POST = withDb(handler);