import { NextRequest, NextResponse } from 'next/server'; import { cookies } from 'next/headers'; import { db } from '@/lib/db'; import { csrfProtection } from '@/lib/csrf'; import { getClientIdentifier, rateLimit, RateLimits } from '@/lib/rateLimit'; import { onboardingUpdateSchema, validateRequest } from '@/lib/validationSchemas'; import { getOnboardingState, triggerLifecycleScoring } from '@/lib/revops-server'; export const dynamic = 'force-dynamic'; export async function GET() { try { const userId = cookies().get('userId')?.value; if (!userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const state = await getOnboardingState(userId); if (!state) { return NextResponse.json({ error: 'User not found' }, { status: 404 }); } return NextResponse.json(state); } catch (error) { console.error('Error fetching onboarding state:', error); return NextResponse.json({ error: 'Failed to fetch onboarding state' }, { status: 500 }); } } export async function PATCH(request: NextRequest) { try { const csrfCheck = csrfProtection(request); if (!csrfCheck.valid) { return NextResponse.json({ error: csrfCheck.error }, { status: 403 }); } const userId = cookies().get('userId')?.value; const clientId = userId || getClientIdentifier(request); const rateLimitResult = rateLimit(clientId, RateLimits.PROFILE_UPDATE); if (!rateLimitResult.success) { return NextResponse.json( { error: 'Too many requests. Please try again later.', retryAfter: Math.ceil((rateLimitResult.reset - Date.now()) / 1000), }, { status: 429 } ); } if (!userId) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const body = await request.json(); const validation = await validateRequest(onboardingUpdateSchema, body); if (!validation.success) { return NextResponse.json(validation.error, { status: 400 }); } const data = validation.data; const now = new Date(); const existingUser = await db.user.findUnique({ where: { id: userId }, select: { onboardingStartedAt: true, sourceConfirmedAt: true, useCaseSelectedAt: true, goalSelectedAt: true, profileCompletedAt: true, }, }); if (!existingUser) { return NextResponse.json({ error: 'User not found' }, { status: 404 }); } await db.user.update({ where: { id: userId }, data: { onboardingStartedAt: existingUser.onboardingStartedAt ?? now, signupSourceSelfReported: data.signupSourceSelfReported, primaryUseCase: data.primaryUseCase, primaryGoal: data.primaryGoal, jobRole: data.jobRole, companyName: data.companyName, companyWebsite: data.companyWebsite, teamSizeBucket: data.teamSizeBucket, sourceConfirmedAt: data.signupSourceSelfReported && !existingUser.sourceConfirmedAt ? now : undefined, useCaseSelectedAt: data.primaryUseCase && !existingUser.useCaseSelectedAt ? now : undefined, goalSelectedAt: data.primaryGoal && !existingUser.goalSelectedAt ? now : undefined, profileCompletedAt: data.markProfileComplete && !existingUser.profileCompletedAt ? now : undefined, }, }); triggerLifecycleScoring(userId, 'onboarding_update'); const state = await getOnboardingState(userId); return NextResponse.json({ success: true, state }); } catch (error) { console.error('Error updating onboarding state:', error); return NextResponse.json({ error: 'Failed to update onboarding state' }, { status: 500 }); } }