import { NextRequest, NextResponse } from 'next/server'; import { withDb } from '@/lib/api-handler'; import { db } from '@/lib/db'; import { z } from 'zod'; import { hashPassword } from '@/lib/auth'; import { getClientIp } from '@/lib/validation'; import { rateLimit } from '@/lib/rate-limit'; const resetPasswordSchema = z.object({ email: z.string().email('Please enter a valid email address'), code: z.string().length(6, 'Code must be exactly 6 digits'), newPassword: z .string() .min(8, 'Password must be at least 8 characters') .refine((pw) => /[A-Z]/.test(pw), 'Password must contain at least one uppercase letter') .refine((pw) => /[a-z]/.test(pw), 'Password must contain at least one lowercase letter') .refine((pw) => /[0-9]/.test(pw), 'Password must contain at least one number'), }); async function handler(request: NextRequest) { try { // ── Rate Limit: 5 per 15 minutes ───────────────────────────────── const ip = getClientIp(request); const { allowed, retryAfterMs } = await rateLimit(`reset-pw:${ip}`, 5, 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 = resetPasswordSchema.safeParse(body); if (!parsed.success) { return NextResponse.json( { error: parsed.error.issues[0].message }, { status: 400 } ); } const { email, code, newPassword } = parsed.data; // ── Find valid reset token ──────────────────────────────────────── const resetToken = await db.resetToken.findFirst({ where: { email, token: code, usedAt: null, expiresAt: { gt: new Date() }, }, }); if (!resetToken) { return NextResponse.json( { error: 'Invalid or expired reset code. Please request a new one.' }, { status: 400 } ); } // ── Find user ───────────────────────────────────────────────────── const user = await db.user.findUnique({ where: { email } }); if (!user) { return NextResponse.json( { error: 'No account found with this email address.' }, { status: 404 } ); } // ── Hash new password and update user ───────────────────────────── const hashedPassword = await hashPassword(newPassword); await db.user.update({ where: { id: user.id }, data: { password: hashedPassword }, }); // ── Mark token as used ──────────────────────────────────────────── await db.resetToken.update({ where: { id: resetToken.id }, data: { usedAt: new Date() }, }); // ── Delete all sessions for the user ────────────────────────────── await db.session.deleteMany({ where: { userId: user.id }, }); return NextResponse.json({ success: true, message: 'Password has been reset successfully. Please log in with your new password.', }); } catch (error) { console.error('[RESET_PASSWORD_ERROR]', error); return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ); } } export const POST = withDb(handler);