diff --git a/src/pages/[locale]/api/hooks/collection-operation.ts b/src/pages/[locale]/api/hooks/collection-operation.ts index a2b256e..5b3287d 100644 --- a/src/pages/[locale]/api/hooks/collection-operation.ts +++ b/src/pages/[locale]/api/hooks/collection-operation.ts @@ -1,6 +1,7 @@ import type { APIRoute } from "astro"; import { Collections, type WebHookMessage } from "src/shared/payload/payload-sdk"; import { + invalidateDataCache, refreshCurrencies, refreshLocales, refreshWebsiteConfig, @@ -17,6 +18,10 @@ export const POST: APIRoute = async ({ request }) => { const message = (await request.json()) as WebHookMessage; console.log("[Webhook] Received message from CMS:", message); + if (message.id) { + await invalidateDataCache(message.id); + } + switch (message.collection) { case Collections.Wordings: await refreshWordings(); diff --git a/src/shared/payload/payload-sdk.ts b/src/shared/payload/payload-sdk.ts index 1489d68..09651ec 100644 --- a/src/shared/payload/payload-sdk.ts +++ b/src/shared/payload/payload-sdk.ts @@ -2128,5 +2128,6 @@ export const getPayloadSDK = ({ await request(payloadApiUrl(Collections.Videos, `id/${id}`)), getRecorderByID: async (id: string): Promise => await request(payloadApiUrl(Collections.Recorders, `id/${id}`)), + request: async (url: string): Promise => await request(url), }; }; diff --git a/src/utils/payload.ts b/src/utils/payload.ts index 7ddd0ff..8b8f06e 100644 --- a/src/utils/payload.ts +++ b/src/utils/payload.ts @@ -8,6 +8,9 @@ import { getPayloadSDK } from "src/shared/payload/payload-sdk"; let token: string | undefined = undefined; let expiration: number | undefined = undefined; +const responseCache = new Map(); +const idsCacheMap = new Map>(); + export const payload = getPayloadSDK({ apiURL: import.meta.env.PAYLOAD_API_URL, email: import.meta.env.PAYLOAD_USER, @@ -30,8 +33,56 @@ export const payload = getPayloadSDK({ console.log("[PayloadSDK] New token set. TTL is", diffInMinutes, "minutes."); }, }, + responseCache: { + get: (url) => { + const cachedResponse = responseCache.get(url); + if (!cachedResponse) { + console.log("[ResponseCaching] No cached response found for", url); + return undefined; + } + console.log("[ResponseCaching] Retrieved cache response for", url); + + return cachedResponse; + }, + set: (url, response) => { + const stringData = JSON.stringify(response); + const regex = /[a-f0-9]{24}/g; + const ids = [...stringData.matchAll(regex)].map((match) => match[0]); + const uniqueIds = [...new Set(ids)]; + + uniqueIds.forEach((id) => { + const current = idsCacheMap.get(id); + if (current) { + current.add(url); + } else { + idsCacheMap.set(id, new Set([url])); + } + }); + + console.log("[ResponseCaching] Caching response for", url); + responseCache.set(url, response); + }, + }, }); +export const invalidateDataCache = async (id: string) => { + const responsesToInvalidate = idsCacheMap.get(id); + if (!responsesToInvalidate) return; + idsCacheMap.delete(id); + + for (const url of responsesToInvalidate) { + responseCache.delete(url); + try { + await payload.request(url); + console.log("[ResponseCaching][Invalidation] Success for", url); + } catch (e) { + console.log("[ResponseCaching][Invalidation] Failure for", url); + } + } + + console.log("[ResponseCaching] There are currently", responseCache.size, "responses in cache."); +}; + type Cache = { locales: Language[]; currencies: string[];