feat(ideas): updateSecondaryProductsAction — atomisch vervangen secundaire producten
Voegt server action toe die secondary_products voor een idee atomisch vervangt: primair product gefilterd, toegankelijkheid gevalideerd via productAccessFilter, deleteMany + createMany in één transactie. Demo-geblokkeerd, Zod-gevalideerd. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4a929b1962
commit
a5afb8c5fd
1 changed files with 59 additions and 0 deletions
|
|
@ -10,6 +10,8 @@ import { revalidatePath } from 'next/cache'
|
||||||
import { cookies } from 'next/headers'
|
import { cookies } from 'next/headers'
|
||||||
import { getIronSession } from 'iron-session'
|
import { getIronSession } from 'iron-session'
|
||||||
|
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
import { prisma } from '@/lib/prisma'
|
import { prisma } from '@/lib/prisma'
|
||||||
import { SessionData, sessionOptions } from '@/lib/session'
|
import { SessionData, sessionOptions } from '@/lib/session'
|
||||||
import { enforceUserRateLimit } from '@/lib/rate-limit'
|
import { enforceUserRateLimit } from '@/lib/rate-limit'
|
||||||
|
|
@ -165,6 +167,63 @@ export async function deleteIdeaAction(id: string): Promise<ActionResult> {
|
||||||
return { success: true }
|
return { success: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Secondary products
|
||||||
|
|
||||||
|
const secondaryProductsSchema = z.object({
|
||||||
|
ideaId: z.string().cuid(),
|
||||||
|
productIds: z.array(z.string().cuid()).max(10),
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function updateSecondaryProductsAction(
|
||||||
|
ideaId: string,
|
||||||
|
productIds: string[],
|
||||||
|
): Promise<ActionResult> {
|
||||||
|
const session = await getSession()
|
||||||
|
if (!session.userId) return { error: 'Niet ingelogd', code: 401 }
|
||||||
|
if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus', code: 403 }
|
||||||
|
|
||||||
|
const parsed = secondaryProductsSchema.safeParse({ ideaId, productIds })
|
||||||
|
if (!parsed.success) return { error: 'Ongeldige invoer', code: 422 }
|
||||||
|
|
||||||
|
const idea = await prisma.idea.findFirst({
|
||||||
|
where: { id: parsed.data.ideaId, user_id: session.userId },
|
||||||
|
select: { id: true, product_id: true },
|
||||||
|
})
|
||||||
|
if (!idea) return { error: 'Idee niet gevonden', code: 404 }
|
||||||
|
|
||||||
|
// Verwijder primair product uit de lijst (mag niet dubbel)
|
||||||
|
const filtered = parsed.data.productIds.filter((pid) => pid !== idea.product_id)
|
||||||
|
|
||||||
|
// Valideer dat alle gevraagde producten toegankelijk zijn voor de user
|
||||||
|
if (filtered.length > 0) {
|
||||||
|
const { productAccessFilter } = await import('@/lib/product-access')
|
||||||
|
const accessible = await prisma.product.findMany({
|
||||||
|
where: { id: { in: filtered }, ...productAccessFilter(session.userId) },
|
||||||
|
select: { id: true },
|
||||||
|
})
|
||||||
|
if (accessible.length !== filtered.length)
|
||||||
|
return { error: 'Een of meer producten zijn niet toegankelijk', code: 403 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomisch: verwijder alle bestaande, voeg nieuwe in
|
||||||
|
await prisma.$transaction([
|
||||||
|
prisma.ideaProduct.deleteMany({ where: { idea_id: idea.id } }),
|
||||||
|
...(filtered.length > 0
|
||||||
|
? [
|
||||||
|
prisma.ideaProduct.createMany({
|
||||||
|
data: filtered.map((pid) => ({ idea_id: idea.id, product_id: pid })),
|
||||||
|
skipDuplicates: true,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
])
|
||||||
|
|
||||||
|
revalidatePath('/ideas/' + idea.id, 'page')
|
||||||
|
revalidatePath('/ideas', 'page')
|
||||||
|
return { success: true }
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Markdown-edits (grill_md & plan_md handmatig fine-tunen)
|
// Markdown-edits (grill_md & plan_md handmatig fine-tunen)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue