From f4891e69324162b1fc63edf4d8ff7e86ab3b3e2c Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev <572431+veikab@users.noreply.github.com> Date: Wed, 25 Feb 2026 09:08:20 +0700 Subject: [PATCH] fix: replace prisma.$use with $extends for Prisma 6 compatibility prisma.$use middleware API was removed in Prisma 6. Switched to $extends query hooks which is the supported approach. Co-Authored-By: Claude Opus 4.6 --- frontend/server/utils/prisma.ts | 101 +++++++++++++++++++------------- 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/frontend/server/utils/prisma.ts b/frontend/server/utils/prisma.ts index cd6b67f..0e8bd93 100644 --- a/frontend/server/utils/prisma.ts +++ b/frontend/server/utils/prisma.ts @@ -5,51 +5,72 @@ declare global { var __prisma: PrismaClient | undefined; } -export const prisma = +// --------------------------------------------------------------------------- +// Auto-sync ClientTimelineEntry for CalendarEvent and FeedCard +// --------------------------------------------------------------------------- +const TIMELINE_MODEL_MAP: Record = { + calendarEvent: "CALENDAR_EVENT", + feedCard: "RECOMMENDATION", +}; + +function timelineHook(model: string) { + const contentType = TIMELINE_MODEL_MAP[model]; + if (!contentType) return undefined; + + const afterMutate = (result: any) => { + if (!result || typeof result !== "object") return; + const row = result as Record; + const teamId = row.teamId as string | undefined; + const contactId = row.contactId as string | undefined; + const id = row.id as string | undefined; + if (!teamId || !contactId || !id) return; + + const datetime = row.startsAt ?? row.happenedAt ?? row.createdAt ?? new Date(); + + basePrisma.clientTimelineEntry + .upsert({ + where: { + teamId_contentType_contentId: { teamId, contentType, contentId: id }, + }, + create: { teamId, contactId, contentType, contentId: id, datetime }, + update: { contactId, datetime }, + select: { id: true }, + }) + .catch(() => {}); + }; + + return { + async create({ args, query }: any) { + const result = await query(args); + afterMutate(result); + return result; + }, + async update({ args, query }: any) { + const result = await query(args); + afterMutate(result); + return result; + }, + async upsert({ args, query }: any) { + const result = await query(args); + afterMutate(result); + return result; + }, + }; +} + +const basePrisma = globalThis.__prisma ?? new PrismaClient({ log: ["error", "warn"], }); -// --------------------------------------------------------------------------- -// Auto-sync ClientTimelineEntry for CalendarEvent, WorkspaceDocument, FeedCard -// --------------------------------------------------------------------------- -const TIMELINE_MODEL_MAP: Record = { - CalendarEvent: "CALENDAR_EVENT", - FeedCard: "RECOMMENDATION", -}; - -prisma.$use(async (params, next) => { - const result = await next(params); - - const contentType = params.model ? TIMELINE_MODEL_MAP[params.model] : undefined; - if (!contentType) return result; - if (params.action !== "create" && params.action !== "update" && params.action !== "upsert") return result; - if (!result || typeof result !== "object") return result; - - const row = result as Record; - const teamId = row.teamId as string | undefined; - const contactId = row.contactId as string | undefined; - const id = row.id as string | undefined; - if (!teamId || !contactId || !id) return result; - - const datetime = row.startsAt ?? row.happenedAt ?? row.createdAt ?? new Date(); - - prisma.clientTimelineEntry - .upsert({ - where: { - teamId_contentType_contentId: { teamId, contentType, contentId: id }, - }, - create: { teamId, contactId, contentType, contentId: id, datetime }, - update: { contactId, datetime }, - select: { id: true }, - }) - .catch(() => {}); - - return result; -}); +export const prisma = basePrisma.$extends({ + query: { + calendarEvent: timelineHook("calendarEvent")!, + feedCard: timelineHook("feedCard")!, + }, +}) as unknown as PrismaClient; if (process.env.NODE_ENV !== "production") { - globalThis.__prisma = prisma; + globalThis.__prisma = basePrisma; } -