Fix milestone sharing previews and publisher recovery
This commit is contained in:
@@ -86,7 +86,7 @@ export async function PATCH(request: NextRequest, { params }: { params: { id: st
|
||||
where: { id: milestone.id },
|
||||
data: { selfSharedAt: now, publicShareApprovedAt: now, shareToken: token, cardData: card, language },
|
||||
});
|
||||
return NextResponse.json({ ok: true, shareToken: token, milestone: clientState(updated) });
|
||||
return NextResponse.json({ ok: true, shareToken: token, shareVersion: now.getTime(), milestone: clientState(updated) });
|
||||
}
|
||||
|
||||
if (!['pending', 'failed', 'revoked'].includes(milestone.brandStatus)) {
|
||||
|
||||
57
src/app/(main)/api/social-milestones/preferences/route.ts
Normal file
57
src/app/(main)/api/social-milestones/preferences/route.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { csrfProtection } from '@/lib/csrf';
|
||||
import { db } from '@/lib/db';
|
||||
import { getSessionUserId } from '@/lib/session';
|
||||
import { getSocialMilestoneThresholds } from '@/lib/social-milestones';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
function testResetAvailable() {
|
||||
return getSocialMilestoneThresholds().some(threshold => threshold < 100);
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const userId = getSessionUserId();
|
||||
if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const user = await db.user.findUnique({ where: { id: userId }, select: { socialPromptOptOut: true } });
|
||||
if (!user) return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
||||
return NextResponse.json({ promptsEnabled: !user.socialPromptOptOut, testResetAvailable: testResetAvailable() });
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest) {
|
||||
const csrf = csrfProtection(request);
|
||||
if (!csrf.valid) return NextResponse.json({ error: csrf.error }, { status: 403 });
|
||||
const userId = getSessionUserId();
|
||||
if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
||||
const body = await request.json().catch(() => null) as { action?: 'enable' | 'disable' | 'reset_test' } | null;
|
||||
if (!body?.action || !['enable', 'disable', 'reset_test'].includes(body.action)) {
|
||||
return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (body.action === 'reset_test') {
|
||||
if (!testResetAvailable()) return NextResponse.json({ error: 'Test reset is not available in this environment' }, { status: 403 });
|
||||
const latest = await db.socialMilestone.findFirst({
|
||||
where: { userId },
|
||||
orderBy: { detectedAt: 'desc' },
|
||||
select: { id: true },
|
||||
});
|
||||
await db.$transaction([
|
||||
db.user.update({ where: { id: userId }, data: { socialPromptOptOut: false } }),
|
||||
...(latest ? [db.socialMilestone.update({
|
||||
where: { id: latest.id },
|
||||
data: {
|
||||
status: 'detected', shownAt: null, respondedAt: null,
|
||||
brandStatus: 'pending', brandApprovedAt: null, claimedAt: null,
|
||||
brandPostedAt: null, brandPostUrl: null, brandPostError: null,
|
||||
consentText: null, withName: false,
|
||||
selfSharedAt: null, publicShareApprovedAt: null, shareToken: null,
|
||||
},
|
||||
})] : []),
|
||||
]);
|
||||
return NextResponse.json({ ok: true, promptsEnabled: true, resetMilestone: Boolean(latest) });
|
||||
}
|
||||
|
||||
const promptsEnabled = body.action === 'enable';
|
||||
await db.user.update({ where: { id: userId }, data: { socialPromptOptOut: !promptsEnabled } });
|
||||
return NextResponse.json({ ok: true, promptsEnabled });
|
||||
}
|
||||
Reference in New Issue
Block a user