Relegate caching and env variables to the frontend

This commit is contained in:
DrMint 2024-05-16 15:08:39 +02:00
parent 18781e701c
commit dcba1b889d
2 changed files with 102 additions and 135 deletions

@ -8,97 +8,8 @@ import {
} from "./constants"; } from "./constants";
import { Currency, Language } from "./types/collections"; import { Currency, Language } from "./types/collections";
class NodeCache {
constructor(_params: any) {}
getTtl(_key: string): number | undefined {
return undefined;
}
get<T>(_key: string): T | undefined {
return undefined;
}
set<T>(_key: string, _value: T, _ttl: number | string) {}
}
// END MOCKING SECTION // END MOCKING SECTION
const REFRESH_FREQUENCY_IN_SEC = 60;
const CACHE = new NodeCache({
checkperiod: REFRESH_FREQUENCY_IN_SEC,
deleteOnExpire: true,
forceString: true,
maxKeys: 1,
});
const TOKEN_KEY = "token";
type PayloadLoginResponse = {
token: string;
exp: number;
};
const refreshToken = async () => {
const loginUrl = payloadApiUrl(Collections.Recorders, "login");
const loginResult = await fetch(loginUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: process.env.PAYLOAD_USER,
password: process.env.PAYLOAD_PASSWORD,
}),
});
logResponse(loginResult);
if (loginResult.status !== 200) {
throw new Error("Unable to login");
}
const loginJson = (await loginResult.json()) as PayloadLoginResponse;
const { token, exp } = loginJson;
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.");
CACHE.set(TOKEN_KEY, token, ttl);
return token;
};
const getToken = async (): Promise<string> => {
const cachedToken = CACHE.get<string>(TOKEN_KEY);
if (cachedToken !== undefined) {
const cachedTokenTtl = CACHE.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;
}
console.log("Refreshing token");
return await refreshToken();
};
const injectAuth = async (init?: RequestInit): Promise<RequestInit> => ({
...init,
headers: {
...init?.headers,
Authorization: `JWT ${await getToken()}`,
},
});
const logResponse = (res: Response) => console.log(res.status, res.statusText, res.url);
const payloadApiUrl = (collection: Collections, endpoint?: string, isGlobal?: boolean): string =>
`${process.env.PAYLOAD_API_URL}/${isGlobal === undefined ? "" : "globals/"}${collection}${endpoint === undefined ? "" : `/${endpoint}`}`;
const request = async (url: string, init?: RequestInit): Promise<Response> => {
const result = await fetch(url, await injectAuth(init));
logResponse(result);
if (result.status !== 200) {
throw new Error("Unhandled fetch error");
}
return result;
};
// SDK and Types
export type EndpointFolder = { export type EndpointFolder = {
slug: string; slug: string;
icon?: string; icon?: string;
@ -537,49 +448,106 @@ export type PayloadImage = PayloadMedia & {
height: number; height: number;
}; };
export const payload = { // SDK
getConfig: async (): Promise<EndpointWebsiteConfig> =>
await (await request(payloadApiUrl(Collections.WebsiteConfig, `config`, true))).json(), type GetPayloadSDKParams = {
getFolder: async (slug: string): Promise<EndpointFolder> => apiURL: string;
await (await request(payloadApiUrl(Collections.Folders, `slug/${slug}`))).json(), email: string;
getLanguages: async (): Promise<Language[]> => password: string;
await (await request(payloadApiUrl(Collections.Languages, `all`))).json(), cache: Cache;
getCurrencies: async (): Promise<Currency[]> => };
await (await request(payloadApiUrl(Collections.Currencies, `all`))).json(),
getWordings: async (): Promise<EndpointWording[]> => type Cache = {
await (await request(payloadApiUrl(Collections.Wordings, `all`))).json(), set: (token: string, expirationTimestamp: number) => void;
getPage: async (slug: string): Promise<EndpointPage> => get: () => string | undefined;
await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(), };
getCollectible: async (slug: string): Promise<EndpointCollectible> =>
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(), const logResponse = (res: Response) => console.log(res.status, res.statusText, res.url);
getCollectibleScans: async (slug: string): Promise<EndpointCollectibleScans> =>
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans`))).json(), export const getPayloadSDK = ({ apiURL, email, password, cache }: GetPayloadSDKParams) => {
getCollectibleScanPage: async ( const refreshToken = async () => {
slug: string, const loginUrl = payloadApiUrl(Collections.Recorders, "login");
index: string const loginResult = await fetch(loginUrl, {
): Promise<EndpointCollectibleScanPage> => method: "POST",
await ( headers: { "Content-Type": "application/json" },
await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans/${index}`)) body: JSON.stringify({ email, password }),
).json(), });
getCollectibleGallery: async (slug: string): Promise<EndpointCollectibleGallery> => logResponse(loginResult);
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery`))).json(),
getCollectibleGalleryImage: async ( if (loginResult.status !== 200) {
slug: string, throw new Error("Unable to login");
index: string }
): Promise<EndpointCollectibleGalleryImage> =>
await ( const { token, exp } = (await loginResult.json()) as {
await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery/${index}`)) token: string;
).json(), exp: number;
getChronologyEvents: async (): Promise<EndpointChronologyEvent[]> => };
await (await request(payloadApiUrl(Collections.ChronologyEvents, `all`))).json(), cache.set(token, exp);
getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> => return token;
await (await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`))).json(), };
getImageByID: async (id: string): Promise<EndpointImage> =>
await (await request(payloadApiUrl(Collections.Images, `id/${id}`))).json(), const payloadApiUrl = (collection: Collections, endpoint?: string, isGlobal?: boolean): string =>
getAudioByID: async (id: string): Promise<EndpointAudio> => `${apiURL}/${isGlobal === undefined ? "" : "globals/"}${collection}${endpoint === undefined ? "" : `/${endpoint}`}`;
await (await request(payloadApiUrl(Collections.Audios, `id/${id}`))).json(),
getVideoByID: async (id: string): Promise<EndpointVideo> => const request = async (url: string): Promise<Response> => {
await (await request(payloadApiUrl(Collections.Videos, `id/${id}`))).json(), const result = await fetch(url, {
getRecorderByID: async (id: string): Promise<EndpointRecorder> => headers: {
await (await request(payloadApiUrl(Collections.Recorders, `id/${id}`))).json(), Authorization: `JWT ${cache.get() ?? (await refreshToken())}`,
},
});
logResponse(result);
if (result.status !== 200) {
throw new Error("Unhandled fetch error");
}
return result;
};
return {
getConfig: async (): Promise<EndpointWebsiteConfig> =>
await (await request(payloadApiUrl(Collections.WebsiteConfig, `config`, true))).json(),
getFolder: async (slug: string): Promise<EndpointFolder> =>
await (await request(payloadApiUrl(Collections.Folders, `slug/${slug}`))).json(),
getLanguages: async (): Promise<Language[]> =>
await (await request(payloadApiUrl(Collections.Languages, `all`))).json(),
getCurrencies: async (): Promise<Currency[]> =>
await (await request(payloadApiUrl(Collections.Currencies, `all`))).json(),
getWordings: async (): Promise<EndpointWording[]> =>
await (await request(payloadApiUrl(Collections.Wordings, `all`))).json(),
getPage: async (slug: string): Promise<EndpointPage> =>
await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(),
getCollectible: async (slug: string): Promise<EndpointCollectible> =>
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(),
getCollectibleScans: async (slug: string): Promise<EndpointCollectibleScans> =>
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans`))).json(),
getCollectibleScanPage: async (
slug: string,
index: string
): Promise<EndpointCollectibleScanPage> =>
await (
await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans/${index}`))
).json(),
getCollectibleGallery: async (slug: string): Promise<EndpointCollectibleGallery> =>
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery`))).json(),
getCollectibleGalleryImage: async (
slug: string,
index: string
): Promise<EndpointCollectibleGalleryImage> =>
await (
await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/gallery/${index}`))
).json(),
getChronologyEvents: async (): Promise<EndpointChronologyEvent[]> =>
await (await request(payloadApiUrl(Collections.ChronologyEvents, `all`))).json(),
getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> =>
await (await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`))).json(),
getImageByID: async (id: string): Promise<EndpointImage> =>
await (await request(payloadApiUrl(Collections.Images, `id/${id}`))).json(),
getAudioByID: async (id: string): Promise<EndpointAudio> =>
await (await request(payloadApiUrl(Collections.Audios, `id/${id}`))).json(),
getVideoByID: async (id: string): Promise<EndpointVideo> =>
await (await request(payloadApiUrl(Collections.Videos, `id/${id}`))).json(),
getRecorderByID: async (id: string): Promise<EndpointRecorder> =>
await (await request(payloadApiUrl(Collections.Recorders, `id/${id}`))).json(),
};
}; };

@ -79,7 +79,6 @@ const start = async () => {
result.push(removeMockingSection(readFileSync(path.join(__dirname, "constants.ts"), "utf-8"))); result.push(removeMockingSection(readFileSync(path.join(__dirname, "constants.ts"), "utf-8")));
result.push("////////////////// SDK //////////////////"); result.push("////////////////// SDK //////////////////");
result.push(`import NodeCache from "node-cache";`);
result.push(removeMockingSection(readFileSync(path.join(__dirname, "sdk.ts"), "utf-8"))); result.push(removeMockingSection(readFileSync(path.join(__dirname, "sdk.ts"), "utf-8")));
res.type("text/plain"); res.type("text/plain");