Added basic caching

This commit is contained in:
DrMint 2024-06-13 13:22:55 +02:00
parent 12d9228502
commit 6c99de5bfb
4 changed files with 73 additions and 72 deletions

20
package-lock.json generated
View File

@ -13,7 +13,6 @@
"accept-language": "^3.0.18", "accept-language": "^3.0.18",
"astro": "4.10.2", "astro": "4.10.2",
"astro-icon": "^1.1.0", "astro-icon": "^1.1.0",
"node-cache": "^5.1.2",
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",
"ua-parser-js": "^1.0.38" "ua-parser-js": "^1.0.38"
}, },
@ -4104,14 +4103,6 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1" "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": { "node_modules/clsx": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@ -6545,17 +6536,6 @@
"url": "https://opencollective.com/unified" "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": { "node_modules/node-releases": {
"version": "2.0.14", "version": "2.0.14",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",

View File

@ -24,7 +24,6 @@
"accept-language": "^3.0.18", "accept-language": "^3.0.18",
"astro": "4.10.2", "astro": "4.10.2",
"astro-icon": "^1.1.0", "astro-icon": "^1.1.0",
"node-cache": "^5.1.2",
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",
"ua-parser-js": "^1.0.38" "ua-parser-js": "^1.0.38"
}, },

View File

@ -1953,17 +1953,23 @@ type GetPayloadSDKParams = {
apiURL: string; apiURL: string;
email: string; email: string;
password: string; password: string;
cache: Cache; tokenCache?: TokenCache;
responseCache?: ResponseCache;
}; };
type Cache = { type TokenCache = {
set: (token: string, expirationTimestamp: number) => void; set: (token: string, expirationTimestamp: number) => void;
get: () => string | undefined; 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); 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 refreshToken = async () => {
const loginUrl = payloadApiUrl(Collections.Recorders, "login"); const loginUrl = payloadApiUrl(Collections.Recorders, "login");
const loginResult = await fetch(loginUrl, { const loginResult = await fetch(loginUrl, {
@ -1981,72 +1987,80 @@ export const getPayloadSDK = ({ apiURL, email, password, cache }: GetPayloadSDKP
token: string; token: string;
exp: number; exp: number;
}; };
cache.set(token, exp); tokenCache?.set(token, exp);
return token; return token;
}; };
const payloadApiUrl = (collection: Collections, endpoint?: string, isGlobal?: boolean): string => const payloadApiUrl = (collection: Collections, endpoint?: string, isGlobal?: boolean): string =>
`${apiURL}/${isGlobal === undefined ? "" : "globals/"}${collection}${endpoint === undefined ? "" : `/${endpoint}`}`; `${apiURL}/${isGlobal === undefined ? "" : "globals/"}${collection}${endpoint === undefined ? "" : `/${endpoint}`}`;
const request = async (url: string): Promise<Response> => { const request = async (url: string): Promise<any> => {
const cachedResponse = responseCache?.get(url)
if (cachedResponse) {
return cachedResponse;
}
const result = await fetch(url, { const result = await fetch(url, {
headers: { headers: {
Authorization: `JWT ${cache.get() ?? (await refreshToken())}`, Authorization: `JWT ${tokenCache?.get() ?? (await refreshToken())}`,
}, },
}); });
logResponse(result); logResponse(result);
if (result.status !== 200) { if (!result.ok) {
throw new Error("Unhandled fetch error"); throw new Error("Unhandled fetch error");
} }
return result; const data = await result.json()
responseCache?.set(url, data);
return data;
}; };
return { return {
getConfig: async (): Promise<EndpointWebsiteConfig> => getConfig: async (): Promise<EndpointWebsiteConfig> =>
await (await request(payloadApiUrl(Collections.WebsiteConfig, `config`, true))).json(), await request(payloadApiUrl(Collections.WebsiteConfig, `config`, true)),
getFolder: async (slug: string): Promise<EndpointFolder> => getFolder: async (slug: string): Promise<EndpointFolder> =>
await (await request(payloadApiUrl(Collections.Folders, `slug/${slug}`))).json(), await request(payloadApiUrl(Collections.Folders, `slug/${slug}`)),
getLanguages: async (): Promise<Language[]> => getLanguages: async (): Promise<Language[]> =>
await (await request(payloadApiUrl(Collections.Languages, `all`))).json(), await request(payloadApiUrl(Collections.Languages, `all`)),
getCurrencies: async (): Promise<Currency[]> => getCurrencies: async (): Promise<Currency[]> =>
await (await request(payloadApiUrl(Collections.Currencies, `all`))).json(), await request(payloadApiUrl(Collections.Currencies, `all`)),
getWordings: async (): Promise<EndpointWording[]> => getWordings: async (): Promise<EndpointWording[]> =>
await (await request(payloadApiUrl(Collections.Wordings, `all`))).json(), await request(payloadApiUrl(Collections.Wordings, `all`)),
getPage: async (slug: string): Promise<EndpointPage> => getPage: async (slug: string): Promise<EndpointPage> =>
await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(), await request(payloadApiUrl(Collections.Pages, `slug/${slug}`)),
getCollectible: async (slug: string): Promise<EndpointCollectible> => getCollectible: async (slug: string): Promise<EndpointCollectible> =>
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(), await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`)),
getCollectibleScans: async (slug: string): Promise<EndpointCollectibleScans> => getCollectibleScans: async (slug: string): Promise<EndpointCollectibleScans> =>
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans`))).json(), await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans`)),
getCollectibleScanPage: async ( getCollectibleScanPage: async (
slug: string, slug: string,
index: string index: string
): Promise<EndpointCollectibleScanPage> => ): Promise<EndpointCollectibleScanPage> =>
await (
await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans/${index}`)) await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans/${index}`))
).json(), ,
getCollectibleGallery: async (slug: string): Promise<EndpointCollectibleGallery> => getCollectibleGallery: async (slug: string): Promise<EndpointCollectibleGallery> =>
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery`))).json(), await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery`)),
getCollectibleGalleryImage: async ( getCollectibleGalleryImage: async (
slug: string, slug: string,
index: string index: string
): Promise<EndpointCollectibleGalleryImage> => ): Promise<EndpointCollectibleGalleryImage> =>
await (
await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery/${index}`)) await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery/${index}`))
).json(), ,
getChronologyEvents: async (): Promise<EndpointChronologyEvent[]> => getChronologyEvents: async (): Promise<EndpointChronologyEvent[]> =>
await (await request(payloadApiUrl(Collections.ChronologyEvents, `all`))).json(), await request(payloadApiUrl(Collections.ChronologyEvents, `all`)),
getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> => getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> =>
await (await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`))).json(), await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`)),
getImageByID: async (id: string): Promise<EndpointImage> => getImageByID: async (id: string): Promise<EndpointImage> =>
await (await request(payloadApiUrl(Collections.Images, `id/${id}`))).json(), await request(payloadApiUrl(Collections.Images, `id/${id}`)),
getAudioByID: async (id: string): Promise<EndpointAudio> => getAudioByID: async (id: string): Promise<EndpointAudio> =>
await (await request(payloadApiUrl(Collections.Audios, `id/${id}`))).json(), await request(payloadApiUrl(Collections.Audios, `id/${id}`)),
getVideoByID: async (id: string): Promise<EndpointVideo> => getVideoByID: async (id: string): Promise<EndpointVideo> =>
await (await request(payloadApiUrl(Collections.Videos, `id/${id}`))).json(), await request(payloadApiUrl(Collections.Videos, `id/${id}`)),
getRecorderByID: async (id: string): Promise<EndpointRecorder> => getRecorderByID: async (id: string): Promise<EndpointRecorder> =>
await (await request(payloadApiUrl(Collections.Recorders, `id/${id}`))).json(), await request(payloadApiUrl(Collections.Recorders, `id/${id}`)),
}; };
}; };

View File

@ -4,39 +4,47 @@ import {
type EndpointWebsiteConfig, type EndpointWebsiteConfig,
} from "src/shared/payload/payload-sdk"; } from "src/shared/payload/payload-sdk";
import { getPayloadSDK } 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; let token: string | undefined = undefined;
const nodeCache = new NodeCache({ let expiration: number | undefined = undefined;
checkperiod: REFRESH_FREQUENCY_IN_SEC,
deleteOnExpire: true, const responseCache = new Map<string, any>();
forceString: true,
maxKeys: 1,
});
const TOKEN_KEY = "token";
export const payload = getPayloadSDK({ export const payload = getPayloadSDK({
apiURL: import.meta.env.PAYLOAD_API_URL, apiURL: import.meta.env.PAYLOAD_API_URL,
email: import.meta.env.PAYLOAD_USER, email: import.meta.env.PAYLOAD_USER,
password: import.meta.env.PAYLOAD_PASSWORD, password: import.meta.env.PAYLOAD_PASSWORD,
cache: { tokenCache: {
get: () => { get: () => {
const cachedToken = nodeCache.get<string>(TOKEN_KEY); if (!token) return undefined;
if (cachedToken !== undefined) { if (!expiration || expiration < Date.now()) {
const cachedTokenTtl = nodeCache.getTtl(TOKEN_KEY) as number; console.log("[PayloadSDK] No token to be retrieved or the token expired");
const diffInMinutes = Math.floor((cachedTokenTtl - Date.now()) / 1000 / 60);
console.log("Retrieved token from cache. TTL is", diffInMinutes, "minutes.");
return cachedToken;
}
console.log("No token to be retrieved or the token expired");
return undefined; 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) => { set: (newToken, newExpiration) => {
const now = Math.floor(Date.now() / 1000); token = newToken;
const ttl = Math.floor(exp - now - REFRESH_FREQUENCY_IN_SEC * 2); expiration = newExpiration * 1000;
const ttlInMinutes = Math.floor(ttl / 60); const diffInMinutes = Math.floor((expiration - Date.now()) / 1000 / 60);
console.log("Token was refreshed. TTL is", ttlInMinutes, "minutes."); console.log("[PayloadSDK] New token set. TTL is", diffInMinutes, "minutes.");
nodeCache.set(TOKEN_KEY, token, ttl); },
},
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);
}, },
}, },
}); });