import { NextResponse } from "next/server"; import { hash } from "bcryptjs"; import crypto from "crypto"; import { z } from "zod"; import { prisma } from "@/lib/prisma"; import { sendEmail } from "@/lib/email"; const registerSchema = z.object({ email: z.email("Invalid email address"), password: z.string().min(8, "Password must be at least 8 characters"), name: z.string().min(1).optional(), }); export async function POST(request: Request) { try { const body: unknown = await request.json(); const parsed = registerSchema.safeParse(body); if (!parsed.success) { return NextResponse.json( { error: parsed.error.issues[0]?.message ?? "Invalid input" }, { status: 400 } ); } const { email, password, name } = parsed.data; const normalizedEmail = email.toLowerCase(); const existing = await prisma.user.findUnique({ where: { email: normalizedEmail }, }); if (existing) { return NextResponse.json( { error: "An account with this email already exists" }, { status: 409 } ); } const passwordHash = await hash(password, 12); const user = await prisma.user.create({ data: { email: normalizedEmail, passwordHash, name: name ?? null, subscription: { create: { tier: "FREE", sessionsLimit: 20, }, }, }, select: { id: true, email: true, name: true, createdAt: true, }, }); // Send verification email (non-blocking — don't fail registration on email errors) try { const rawToken = crypto.randomBytes(32).toString("hex"); const tokenHash = crypto .createHash("sha256") .update(rawToken) .digest("hex"); await prisma.emailVerificationToken.create({ data: { userId: user.id, token: tokenHash, expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours }, }); const verifyUrl = `https://agentlens.vectry.tech/verify-email?token=${rawToken}`; await sendEmail({ to: user.email, subject: "Verify your AgentLens email", html: `
Thanks for signing up for AgentLens. Click the link below to verify your email address.
Verify EmailThis link expires in 24 hours. If you didn't create an account, you can safely ignore this email.