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"; import { checkRateLimit, AUTH_RATE_LIMITS } from "@/lib/rate-limit"; 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 ip = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? "unknown"; const rl = await checkRateLimit(`register:${ip}`, AUTH_RATE_LIMITS.register); if (!rl.allowed) { return NextResponse.json( { error: "Too many registration attempts. Please try again later." }, { status: 429, headers: { "Retry-After": String(Math.ceil((rl.resetAt - Date.now()) / 1000)) } } ); } 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( { message: "If this email is available, a confirmation email will be sent." }, { status: 200 } ); } 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, }, }); 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), }, }); 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.