From e6fc428b15076b496b0a283294e312bb4eb0cc65 Mon Sep 17 00:00:00 2001 From: knuthtimo-lab Date: Wed, 29 Jul 2026 10:52:29 +0200 Subject: [PATCH] Email marketing V3 --- src/app/(main)/api/auth/signup/route.ts | 58 ++++++++++++++++--------- src/lib/auth.ts | 2 +- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/app/(main)/api/auth/signup/route.ts b/src/app/(main)/api/auth/signup/route.ts index ae76271..6762c80 100644 --- a/src/app/(main)/api/auth/signup/route.ts +++ b/src/app/(main)/api/auth/signup/route.ts @@ -13,9 +13,31 @@ import { getEmailDomain, parseAttributionCookie, } from '@/lib/revops'; -import { triggerLifecycleScoring } from '@/lib/revops-server'; - -export async function POST(request: NextRequest) { +import { triggerLifecycleScoring } from '@/lib/revops-server'; + +async function issueVerificationEmail(user: { email: string; name: string | null }) { + const verificationToken = crypto.randomBytes(32).toString('base64url'); + const verificationUrl = new URL('/api/auth/verify-email', process.env.NEXT_PUBLIC_APP_URL || 'https://www.qrmaster.net'); + verificationUrl.searchParams.set('token', verificationToken); + + await db.verificationToken.deleteMany({ where: { identifier: user.email } }); + await db.verificationToken.create({ + data: { + identifier: user.email, + token: verificationToken, + expires: new Date(Date.now() + 24 * 60 * 60 * 1000), + }, + }); + + try { + await sendEmailVerificationEmail(user.email, user.name ?? 'there', verificationUrl.toString()); + } catch (error) { + await db.verificationToken.deleteMany({ where: { token: verificationToken } }); + throw error; + } +} + +export async function POST(request: NextRequest) { try { // CSRF Protection const csrfCheck = csrfProtection(request); @@ -62,8 +84,18 @@ export async function POST(request: NextRequest) { where: { email }, }); - if (existingUser) { - return NextResponse.json( + if (existingUser) { + if (!existingUser.emailVerified && existingUser.password && await bcrypt.compare(password, existingUser.password)) { + try { + await issueVerificationEmail(existingUser); + return NextResponse.json({ success: true, requiresEmailVerification: true, email: existingUser.email }); + } catch (emailError) { + console.error('Email verification resend failed:', emailError); + return NextResponse.json({ error: 'We could not send the confirmation email. Please try again.' }, { status: 503 }); + } + } + + return NextResponse.json( { error: 'User already exists' }, { status: 400 } ); @@ -96,25 +128,11 @@ export async function POST(request: NextRequest) { triggerLifecycleScoring(user.id, 'signup'); - const verificationToken = crypto.randomBytes(32).toString('base64url'); - const verificationUrl = new URL('/api/auth/verify-email', process.env.NEXT_PUBLIC_APP_URL || 'https://www.qrmaster.net'); - verificationUrl.searchParams.set('token', verificationToken); - - await db.verificationToken.deleteMany({ where: { identifier: user.email } }); - await db.verificationToken.create({ - data: { - identifier: user.email, - token: verificationToken, - expires: new Date(Date.now() + 24 * 60 * 60 * 1000), - }, - }); - // A confirmed address is required before the account can be used or receive campaigns. try { - await sendEmailVerificationEmail(user.email, user.name ?? 'there', verificationUrl.toString()); + await issueVerificationEmail(user); } catch (emailError) { console.error('Email verification message failed:', emailError); - await db.verificationToken.deleteMany({ where: { token: verificationToken } }); await db.user.delete({ where: { id: user.id } }); return NextResponse.json({ error: 'We could not send the confirmation email. Please try again.' }, { status: 503 }); } diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 64b61d3..e132f2a 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -35,7 +35,7 @@ export const authOptions: NextAuthOptions = { // confirm their email, and cannot sign in before doing so. if (!user.emailVerified) { const pendingVerification = await db.verificationToken.findFirst({ - where: { identifier: user.email, expires: { gt: new Date() } }, + where: { identifier: user.email }, select: { token: true }, }); if (pendingVerification) return null;