import type { ActionFunctionArgs } from "react-router"; import { authenticate } from "../shopify.server"; import db from "../db.server"; // products/update: refresh cached titles, handles and images used by the admin and the storefront. // products/delete, collections/delete: remove them from offers and log it in the offer history. // A published fixed bundle that lost a product is unpublished so the merchant reviews it. export const action = async ({ request }: ActionFunctionArgs) => { const { shop, topic, payload } = await authenticate.webhook(request); const row = await db.shop.findUnique({ where: { domain: shop } }); if (!row) return new Response(); const p = payload as { id?: number | string; title?: string; handle?: string; image?: { src?: string } | null; images?: { src?: string }[] }; const id = String(p.id ?? ""); if (!id) return new Response(); const offerFilter = { offer: { shopId: row.id } }; if (topic === "PRODUCTS_UPDATE") { const image = p.image?.src || p.images?.[0]?.src || null; await db.offerLine.updateMany({ where: { ...offerFilter, productId: id, variantId: null }, data: { title: p.title } }); await db.offerLine.updateMany({ where: { ...offerFilter, productId: id }, data: { handle: p.handle, imageUrl: image } }); await db.offerScope.updateMany({ where: { ...offerFilter, kind: "product", refId: id }, data: { title: p.title, handle: p.handle, imageUrl: image } }); await db.offerScope.updateMany({ where: { ...offerFilter, kind: "variant", parentId: id }, data: { handle: p.handle, imageUrl: image } }); await db.shop.update({ where: { id: row.id }, data: { nextSyncAt: new Date() } }); return new Response(); } const isProduct = topic === "PRODUCTS_DELETE"; const lines = isProduct ? await db.offerLine.findMany({ where: { ...offerFilter, productId: id } }) : []; const scopes = await db.offerScope.findMany({ where: isProduct ? { ...offerFilter, OR: [{ kind: "product", refId: id }, { kind: "variant", parentId: id }] } : { ...offerFilter, kind: "collection", refId: id }, }); if (!lines.length && !scopes.length) return new Response(); await db.offerLine.deleteMany({ where: { id: { in: lines.map((l) => l.id) } } }); await db.offerScope.deleteMany({ where: { id: { in: scopes.map((s) => s.id) } } }); const affected = [...new Set([...lines.map((l) => l.offerId), ...scopes.map((s) => s.offerId)])]; await db.auditLog.createMany({ data: affected.map((offerId) => ({ shopId: row.id, offerId, field: isProduct ? "product deleted" : "collection deleted", oldValue: lines.find((l) => l.offerId === offerId)?.title || scopes.find((s) => s.offerId === offerId)?.title || id, newValue: "removed from the offer", })), }); if (isProduct) { await db.offer.updateMany({ where: { id: { in: affected }, type: "fixed", published: true }, data: { published: false } }); } await db.shop.update({ where: { id: row.id }, data: { nextSyncAt: new Date() } }); return new Response(); };