import { randomBytes } from 'crypto'; import { NextRequest, NextResponse } from 'next/server'; import { db } from '@/lib/db'; import { getWwwOrigin } from '@/lib/hosts'; import { getSessionUserId } from '@/lib/session'; import { buildMilestonePostForQr, milestoneThreshold, socialLocale } from '@/lib/social-milestones'; import { ensureSocialMilestoneCard } from '@/lib/social-milestones-server'; export const dynamic = 'force-dynamic'; // Returns at most one item. A missing response is treated as no consent, never as approval. export async function GET(request: NextRequest) { const userId = getSessionUserId(); if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); const user = await db.user.findUnique({ where: { id: userId }, select: { socialPromptOptOut: true, xHandle: true, primaryUseCase: true }, }); if (!user || user.socialPromptOptOut) return NextResponse.json({ milestone: null }); const milestone = await db.socialMilestone.findFirst({ where: { userId, OR: [ { status: { in: ['detected', 'shown'] } }, { status: 'approved', brandStatus: { in: ['approved', 'processing', 'failed'] } }, ], }, orderBy: { detectedAt: 'asc' }, include: { qr: { select: { id: true, title: true, createdAt: true } } }, }); if (!milestone) return NextResponse.json({ milestone: null }); if (milestone.status === 'detected') { await db.socialMilestone.update({ where: { id: milestone.id }, data: { status: 'shown', shownAt: new Date() } }); } const threshold = milestoneThreshold(milestone.kind); if (!threshold) return NextResponse.json({ milestone: null }); const locale = socialLocale(request.nextUrl.searchParams.get('locale')); const shareToken = milestone.shareToken || randomBytes(9).toString('base64url'); if (!milestone.shareToken) { await db.socialMilestone.update({ where: { id: milestone.id }, data: { shareToken } }); } const shareUrl = `${getWwwOrigin()}/s/m/${shareToken}`; const card = await ensureSocialMilestoneCard({ milestoneId: milestone.id, cardData: milestone.cardData, kind: milestone.kind, detectedAt: milestone.detectedAt, language: locale, qr: milestone.qr, primaryUseCase: user.primaryUseCase, refresh: ['detected', 'shown'].includes(milestone.status), snapshotAt: new Date(), }); return NextResponse.json({ milestone: { id: milestone.id, qrTitle: milestone.qr.title, threshold, defaultXHandle: user.xHandle, brandStatus: milestone.brandStatus, brandPostUrl: milestone.brandPostUrl, brandPostError: milestone.brandPostError, language: locale, shareUrl, preview: buildMilestonePostForQr( user.primaryUseCase, card.totalUniqueScans || threshold, null, locale, milestone.qr.title, ), card, }, }); }