Added basic caching
This commit is contained in:
parent
12d9228502
commit
6c99de5bfb
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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}`)),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue