import { NextResponse } from "next/server"; import crypto from "crypto"; import { prisma } from "@/lib/prisma"; import { sendEmail } from "@/lib/email"; import { rateLimit } from "@/lib/rateLimit"; export async function POST(req: Request) { // Rate limit: max 5 forgot password requests per 15 mins per IP const rateLimitResponse = rateLimit(req, { limit: 5, windowMs: 15 * 60 * 1000 }); if (rateLimitResponse) return rateLimitResponse; try { const { email } = await req.json(); if (!email || typeof email !== "string") { return NextResponse.json({ error: "Email is required" }, { status: 400 }); } const cleanEmail = email.toLowerCase().trim(); const user = await prisma.user.findUnique({ where: { email: cleanEmail }, }); // Return generic message regardless of user existence to prevent email enumeration const genericSuccess = NextResponse.json({ success: true, message: "If an account exists for this email, password reset instructions have been sent.", }); if (!user) { return genericSuccess; } // Generate crypto token valid for 1 hour const token = crypto.randomBytes(32).toString("hex"); const expiresAt = new Date(Date.now() + 60 * 60 * 1000); await prisma.passwordResetToken.create({ data: { userId: user.id, token, expiresAt, }, }); const baseUrl = process.env.NEXTAUTH_URL || "http://localhost:3000"; const resetUrl = `${baseUrl}/reset-password?token=${token}`; await sendEmail({ to: user.email, subject: "🔒 JobsBoard: Reset Your Password", html: `

Password Reset Request

Hi ${user.name || "there"}, we received a request to reset your password for your JobsBoard account.

Click the button below to set a new password. This link is valid for 1 hour and can only be used once.

Reset Password ↗

If you did not request a password reset, you can safely ignore this email. Your password will remain unchanged.

`, }); return genericSuccess; } catch (err: any) { console.error("POST /api/auth/forgot-password error:", err); return NextResponse.json({ error: "Failed to process request" }, { status: 500 }); } }