import { NextResponse } from "next/server"; import { createHash } from "crypto"; import { hash } from "bcryptjs"; import { z } from "zod"; import { prisma } from "@/lib/prisma"; const resetPasswordSchema = z.object({ token: z.string().min(1, "Token is required"), password: z.string().min(8, "Password must be at least 8 characters"), }); function hashToken(token: string): string { return createHash("sha256").update(token).digest("hex"); } export async function POST(request: Request) { try { const body: unknown = await request.json(); const parsed = resetPasswordSchema.safeParse(body); if (!parsed.success) { return NextResponse.json( { error: parsed.error.issues[0]?.message ?? "Invalid input" }, { status: 400 } ); } const { token, password } = parsed.data; const tokenHash = hashToken(token); const resetToken = await prisma.passwordResetToken.findUnique({ where: { token: tokenHash }, include: { user: true }, }); if (!resetToken || resetToken.used || resetToken.expiresAt < new Date()) { return NextResponse.json( { error: "Invalid or expired reset link" }, { status: 400 } ); } const passwordHash = await hash(password, 12); await prisma.$transaction([ prisma.user.update({ where: { id: resetToken.userId }, data: { passwordHash }, }), prisma.passwordResetToken.update({ where: { id: resetToken.id }, data: { used: true }, }), ]); return NextResponse.json({ success: true }); } catch { return NextResponse.json( { error: "Internal server error" }, { status: 500 } ); } }