diff --git a/package-lock.json b/package-lock.json index f77363b..312ddcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "accept-language": "^3.0.18", "astro": "4.10.2", "astro-icon": "^1.1.0", - "node-cache": "^5.1.2", "tippy.js": "^6.3.7", "ua-parser-js": "^1.0.38" }, @@ -4104,14 +4103,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -6545,17 +6536,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/node-cache": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", - "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", - "dependencies": { - "clone": "2.x" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", diff --git a/package.json b/package.json index ac46403..a02ba03 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "accept-language": "^3.0.18", "astro": "4.10.2", "astro-icon": "^1.1.0", - "node-cache": "^5.1.2", "tippy.js": "^6.3.7", "ua-parser-js": "^1.0.38" }, diff --git a/src/shared/payload/payload-sdk.ts b/src/shared/payload/payload-sdk.ts index 7fdfc71..cb70b66 100644 --- a/src/shared/payload/payload-sdk.ts +++ b/src/shared/payload/payload-sdk.ts @@ -1953,17 +1953,23 @@ type GetPayloadSDKParams = { apiURL: string; email: string; password: string; - cache: Cache; + tokenCache?: TokenCache; + responseCache?: ResponseCache; }; -type Cache = { +type TokenCache = { set: (token: string, expirationTimestamp: number) => void; get: () => string | undefined; }; +type ResponseCache = { + set: (url: string, response: any) => void; + get: (url: string) => any | undefined; +}; + const logResponse = (res: Response) => console.log(res.status, res.statusText, res.url); -export const getPayloadSDK = ({ apiURL, email, password, cache }: GetPayloadSDKParams) => { +export const getPayloadSDK = ({ apiURL, email, password, tokenCache, responseCache }: GetPayloadSDKParams) => { const refreshToken = async () => { const loginUrl = payloadApiUrl(Collections.Recorders, "login"); const loginResult = await fetch(loginUrl, { @@ -1981,72 +1987,80 @@ export const getPayloadSDK = ({ apiURL, email, password, cache }: GetPayloadSDKP token: string; exp: number; }; - cache.set(token, exp); + tokenCache?.set(token, exp); return token; }; const payloadApiUrl = (collection: Collections, endpoint?: string, isGlobal?: boolean): string => `${apiURL}/${isGlobal === undefined ? "" : "globals/"}${collection}${endpoint === undefined ? "" : `/${endpoint}`}`; - const request = async (url: string): Promise => { + const request = async (url: string): Promise => { + const cachedResponse = responseCache?.get(url) + if (cachedResponse) { + return cachedResponse; + } + const result = await fetch(url, { headers: { - Authorization: `JWT ${cache.get() ?? (await refreshToken())}`, + Authorization: `JWT ${tokenCache?.get() ?? (await refreshToken())}`, }, }); logResponse(result); - if (result.status !== 200) { + if (!result.ok) { throw new Error("Unhandled fetch error"); } - return result; + const data = await result.json() + responseCache?.set(url, data); + + return data; }; return { getConfig: async (): Promise => - await (await request(payloadApiUrl(Collections.WebsiteConfig, `config`, true))).json(), + await request(payloadApiUrl(Collections.WebsiteConfig, `config`, true)), getFolder: async (slug: string): Promise => - await (await request(payloadApiUrl(Collections.Folders, `slug/${slug}`))).json(), + await request(payloadApiUrl(Collections.Folders, `slug/${slug}`)), getLanguages: async (): Promise => - await (await request(payloadApiUrl(Collections.Languages, `all`))).json(), + await request(payloadApiUrl(Collections.Languages, `all`)), getCurrencies: async (): Promise => - await (await request(payloadApiUrl(Collections.Currencies, `all`))).json(), + await request(payloadApiUrl(Collections.Currencies, `all`)), getWordings: async (): Promise => - await (await request(payloadApiUrl(Collections.Wordings, `all`))).json(), + await request(payloadApiUrl(Collections.Wordings, `all`)), getPage: async (slug: string): Promise => - await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(), + await request(payloadApiUrl(Collections.Pages, `slug/${slug}`)), getCollectible: async (slug: string): Promise => - await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(), + await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`)), getCollectibleScans: async (slug: string): Promise => - await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans`))).json(), + await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans`)), getCollectibleScanPage: async ( slug: string, index: string ): Promise => - await ( + await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans/${index}`)) - ).json(), + , getCollectibleGallery: async (slug: string): Promise => - await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery`))).json(), + await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery`)), getCollectibleGalleryImage: async ( slug: string, index: string ): Promise => - await ( + await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery/${index}`)) - ).json(), + , getChronologyEvents: async (): Promise => - await (await request(payloadApiUrl(Collections.ChronologyEvents, `all`))).json(), + await request(payloadApiUrl(Collections.ChronologyEvents, `all`)), getChronologyEventByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`))).json(), + await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`)), getImageByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.Images, `id/${id}`))).json(), + await request(payloadApiUrl(Collections.Images, `id/${id}`)), getAudioByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.Audios, `id/${id}`))).json(), + await request(payloadApiUrl(Collections.Audios, `id/${id}`)), getVideoByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.Videos, `id/${id}`))).json(), + await request(payloadApiUrl(Collections.Videos, `id/${id}`)), getRecorderByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.Recorders, `id/${id}`))).json(), + await request(payloadApiUrl(Collections.Recorders, `id/${id}`)), }; }; diff --git a/src/utils/payload.ts b/src/utils/payload.ts index fbc2471..645bde5 100644 --- a/src/utils/payload.ts +++ b/src/utils/payload.ts @@ -4,39 +4,47 @@ import { type EndpointWebsiteConfig, } from "src/shared/payload/payload-sdk"; import { getPayloadSDK } from "src/shared/payload/payload-sdk"; -import NodeCache from "node-cache"; -const REFRESH_FREQUENCY_IN_SEC = 60; -const nodeCache = new NodeCache({ - checkperiod: REFRESH_FREQUENCY_IN_SEC, - deleteOnExpire: true, - forceString: true, - maxKeys: 1, -}); -const TOKEN_KEY = "token"; +let token: string | undefined = undefined; +let expiration: number | undefined = undefined; + +const responseCache = new Map(); export const payload = getPayloadSDK({ apiURL: import.meta.env.PAYLOAD_API_URL, email: import.meta.env.PAYLOAD_USER, password: import.meta.env.PAYLOAD_PASSWORD, - cache: { + tokenCache: { get: () => { - const cachedToken = nodeCache.get(TOKEN_KEY); - if (cachedToken !== undefined) { - const cachedTokenTtl = nodeCache.getTtl(TOKEN_KEY) as number; - const diffInMinutes = Math.floor((cachedTokenTtl - Date.now()) / 1000 / 60); - console.log("Retrieved token from cache. TTL is", diffInMinutes, "minutes."); - return cachedToken; + if (!token) return undefined; + if (!expiration || expiration < Date.now()) { + console.log("[PayloadSDK] No token to be retrieved or the token expired"); + return undefined; } - console.log("No token to be retrieved or the token expired"); - return undefined; + const diffInMinutes = Math.floor((expiration - Date.now()) / 1000 / 60); + console.log("[PayloadSDK] Retrieved token from cache. TTL is", diffInMinutes, "minutes."); + return token; }, - set: (token, exp) => { - const now = Math.floor(Date.now() / 1000); - const ttl = Math.floor(exp - now - REFRESH_FREQUENCY_IN_SEC * 2); - const ttlInMinutes = Math.floor(ttl / 60); - console.log("Token was refreshed. TTL is", ttlInMinutes, "minutes."); - nodeCache.set(TOKEN_KEY, token, ttl); + set: (newToken, newExpiration) => { + token = newToken; + expiration = newExpiration * 1000; + const diffInMinutes = Math.floor((expiration - Date.now()) / 1000 / 60); + 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) => { + console.log("[ResponseCaching] Caching response for", url); + responseCache.set(url, response); }, }, });