import { NextRequest, NextResponse } from 'next/server'; import { z } from 'zod'; import { eq } from 'drizzle-orm'; import { randomBytes } from 'crypto'; import { db } from '@/lib/db'; import { users, passwordResetTokens } from '@/lib/db/schema'; import { sendEmail } from '@/lib/email'; import { passwordResetEmail } from '@/lib/email/templates'; const Schema = z.object({ email: z.string().email() }); export async function POST(req: NextRequest): Promise { let body: unknown; try { body = await req.json(); } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }); } const parsed = Schema.safeParse(body); if (!parsed.success) return NextResponse.json({ error: 'Valid email required' }, { status: 400 }); // Always return 200 — don't reveal whether email exists const [user] = await db .select({ id: users.id }) .from(users) .where(eq(users.email, parsed.data.email.toLowerCase())) .limit(1); if (user) { const token = randomBytes(32).toString('hex'); const expires = new Date(Date.now() + 3_600_000); // 1 hour await db .delete(passwordResetTokens) .where(eq(passwordResetTokens.userId, user.id)); await db.insert(passwordResetTokens).values({ token, userId: user.id, expires }); const { subject, html, text } = passwordResetEmail(token); void sendEmail({ to: parsed.data.email, subject, html, text }).catch(() => {}); } return NextResponse.json({ ok: true }); }