| import { NextRequest, NextResponse } from 'next/server'; |
| import { withDb } from '@/lib/api-handler'; |
| import { db } from '@/lib/db'; |
| import { signupSchema, getClientIp } from '@/lib/validation'; |
| import { |
| hashPassword, |
| createSession, |
| generateApiKey, |
| SESSION_COOKIE_NAME, |
| } from '@/lib/auth'; |
| import { authRateLimit } from '@/lib/rate-limit'; |
| import { logActivity } from '@/lib/activity'; |
|
|
| async function handler(request: NextRequest) { |
| try { |
| |
| const ip = getClientIp(request); |
| const { allowed, retryAfterMs } = await authRateLimit(`auth:${ip}`); |
| if (!allowed) { |
| return NextResponse.json( |
| { error: 'Too many signup attempts. Please try again later.' }, |
| { |
| status: 429, |
| headers: { 'Retry-After': String(Math.ceil(retryAfterMs / 1000)) }, |
| } |
| ); |
| } |
|
|
| |
| const body = await request.json(); |
| const parsed = signupSchema.safeParse(body); |
| if (!parsed.success) { |
| return NextResponse.json( |
| { error: parsed.error.issues[0].message }, |
| { status: 400 } |
| ); |
| } |
|
|
| const { name, email, password } = parsed.data; |
|
|
| |
| const existing = await db.user.findUnique({ where: { email } }); |
| if (existing) { |
| return NextResponse.json( |
| { error: 'An account with this email already exists' }, |
| { status: 409 } |
| ); |
| } |
|
|
| |
| const [hashedPassword, apiKey] = await Promise.all([ |
| hashPassword(password), |
| generateApiKey(), |
| ]); |
|
|
| |
| const user = await db.user.create({ |
| data: { |
| name, |
| email, |
| password: hashedPassword, |
| apiKey, |
| plan: 'free', |
| }, |
| }); |
|
|
| |
| const token = await createSession(user.id); |
|
|
| |
| const forwardedProto = request.headers.get('x-forwarded-proto'); |
| const isSecure = forwardedProto === 'https' || process.env.NODE_ENV === 'production'; |
|
|
| |
| const response = NextResponse.json({ |
| user: { |
| id: user.id, |
| email: user.email, |
| name: user.name, |
| plan: user.plan, |
| createdAt: user.createdAt, |
| }, |
| }); |
|
|
| const host = request.headers.get('host') || ''; |
| const domain = host.includes('localhost') ? undefined : `.${host.split(':')[0]}`; |
|
|
| response.cookies.set(SESSION_COOKIE_NAME, token, { |
| httpOnly: true, |
| secure: isSecure, |
| sameSite: 'lax', |
| path: '/', |
| maxAge: 7 * 24 * 60 * 60, |
| ...(domain ? { domain } : {}), |
| }); |
|
|
| logActivity(user.id, 'signup', 'Account created', user.email); |
|
|
| return response; |
| } catch (error) { |
| console.error('[SIGNUP_ERROR]', error); |
| return NextResponse.json( |
| { error: 'Internal server error' }, |
| { status: 500 } |
| ); |
| } |
| } |
|
|
| export const POST = withDb(handler); |