diff --git a/.env.example b/.env.example index bb67db43f..0711fd939 100644 --- a/.env.example +++ b/.env.example @@ -6,3 +6,4 @@ SHOPWARE_STORE_DOMAIN="" SHOPWARE_API_TYPE="store-api" SHOPWARE_ACCESS_TOKEN="" SHOPWARE_USE_SEO_URLS="false" +SHOPWARE_REVALIDATION_SECRET="" diff --git a/app/api/revalidate/route.ts b/app/api/revalidate/route.ts index 47af2a4a4..ba8fad362 100644 --- a/app/api/revalidate/route.ts +++ b/app/api/revalidate/route.ts @@ -1,4 +1,4 @@ -import { revalidate } from 'lib/shopify'; +import { revalidate } from 'lib/shopware'; import { NextRequest, NextResponse } from 'next/server'; export const runtime = 'edge'; diff --git a/lib/constants.ts b/lib/constants.ts index 99711221a..f6fb738b6 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -27,4 +27,3 @@ export const TAGS = { export const HIDDEN_PRODUCT_TAG = 'nextjs-frontend-hidden'; export const DEFAULT_OPTION = 'Default Title'; -export const SHOPIFY_GRAPHQL_API_ENDPOINT = '/api/2023-01/graphql.json'; diff --git a/lib/shopware/index.ts b/lib/shopware/index.ts index ef402e133..21a4b0384 100644 --- a/lib/shopware/index.ts +++ b/lib/shopware/index.ts @@ -1,3 +1,7 @@ +import { headers } from 'next/headers'; +import { NextRequest, NextResponse } from 'next/server'; +import { revalidateTag } from 'next/cache'; +import { TAGS } from 'lib/constants'; import { requestCategory, requestCategoryList, @@ -291,3 +295,40 @@ export async function getProductRecommendations(productId: string): Promise { + return NextResponse.json({ + status: 200, + message: 'This is currently not working and was never tested.', + now: Date.now() + }); + // We always need to respond with a 200 status code, + // otherwise it will continue to retry the request. + const collectionWebhooks = ['collections/create', 'collections/delete', 'collections/update']; + const productWebhooks = ['products/create', 'products/delete', 'products/update']; + const topic = headers().get('x-shopware-topic') || 'unknown'; + const secret = req.nextUrl.searchParams.get('secret'); + const isCollectionUpdate = collectionWebhooks.includes(topic); + const isProductUpdate = productWebhooks.includes(topic); + + if (!secret || secret !== process.env.SHOPWARE_REVALIDATION_SECRET) { + console.error('Invalid revalidation secret.'); + return NextResponse.json({ status: 200 }); + } + + if (!isCollectionUpdate && !isProductUpdate) { + // We don't need to revalidate anything for any other topics. + return NextResponse.json({ status: 200 }); + } + + if (isCollectionUpdate) { + revalidateTag(TAGS.collections); + } + + if (isProductUpdate) { + revalidateTag(TAGS.products); + } + + return NextResponse.json({ status: 200, revalidated: true, now: Date.now() }); +} diff --git a/lib/type-guards.ts b/lib/type-guards.ts index 1b7e7af5a..a8a615003 100644 --- a/lib/type-guards.ts +++ b/lib/type-guards.ts @@ -1,26 +1,3 @@ -export interface ShopifyErrorLike { - status: number; - message: Error; -} - export const isObject = (object: unknown): object is Record => { return typeof object === 'object' && object !== null && !Array.isArray(object); }; - -export const isShopifyError = (error: unknown): error is ShopifyErrorLike => { - if (!isObject(error)) return false; - - if (error instanceof Error) return true; - - return findError(error); -}; - -function findError(error: T): boolean { - if (Object.prototype.toString.call(error) === '[object Error]') { - return true; - } - - const prototype = Object.getPrototypeOf(error) as T | null; - - return prototype === null ? false : findError(prototype); -}