diff --git a/src/sdk.ts b/src/sdk.ts index 825662c..5fd838e 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -8,97 +8,8 @@ import { } from "./constants"; import { Currency, Language } from "./types/collections"; -class NodeCache { - constructor(_params: any) {} - getTtl(_key: string): number | undefined { - return undefined; - } - get(_key: string): T | undefined { - return undefined; - } - set(_key: string, _value: T, _ttl: number | string) {} -} - // 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 => { - const cachedToken = CACHE.get(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 => ({ - ...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 => { - 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 = { slug: string; icon?: string; @@ -537,49 +448,106 @@ export type PayloadImage = PayloadMedia & { height: number; }; -export const payload = { - getConfig: async (): Promise => - await (await request(payloadApiUrl(Collections.WebsiteConfig, `config`, true))).json(), - getFolder: async (slug: string): Promise => - await (await request(payloadApiUrl(Collections.Folders, `slug/${slug}`))).json(), - getLanguages: async (): Promise => - await (await request(payloadApiUrl(Collections.Languages, `all`))).json(), - getCurrencies: async (): Promise => - await (await request(payloadApiUrl(Collections.Currencies, `all`))).json(), - getWordings: async (): Promise => - await (await request(payloadApiUrl(Collections.Wordings, `all`))).json(), - getPage: async (slug: string): Promise => - await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(), - getCollectible: async (slug: string): Promise => - await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(), - getCollectibleScans: async (slug: string): Promise => - await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans`))).json(), - 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(), - 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(), - getChronologyEventByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`))).json(), - getImageByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.Images, `id/${id}`))).json(), - getAudioByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.Audios, `id/${id}`))).json(), - getVideoByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.Videos, `id/${id}`))).json(), - getRecorderByID: async (id: string): Promise => - await (await request(payloadApiUrl(Collections.Recorders, `id/${id}`))).json(), +// SDK + +type GetPayloadSDKParams = { + apiURL: string; + email: string; + password: string; + cache: Cache; +}; + +type Cache = { + set: (token: string, expirationTimestamp: number) => void; + get: () => string | undefined; +}; + +const logResponse = (res: Response) => console.log(res.status, res.statusText, res.url); + +export const getPayloadSDK = ({ apiURL, email, password, cache }: GetPayloadSDKParams) => { + 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, password }), + }); + logResponse(loginResult); + + if (loginResult.status !== 200) { + throw new Error("Unable to login"); + } + + const { token, exp } = (await loginResult.json()) as { + token: string; + exp: number; + }; + cache.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 result = await fetch(url, { + headers: { + Authorization: `JWT ${cache.get() ?? (await refreshToken())}`, + }, + }); + logResponse(result); + + if (result.status !== 200) { + throw new Error("Unhandled fetch error"); + } + + return result; + }; + + return { + getConfig: async (): Promise => + await (await request(payloadApiUrl(Collections.WebsiteConfig, `config`, true))).json(), + getFolder: async (slug: string): Promise => + await (await request(payloadApiUrl(Collections.Folders, `slug/${slug}`))).json(), + getLanguages: async (): Promise => + await (await request(payloadApiUrl(Collections.Languages, `all`))).json(), + getCurrencies: async (): Promise => + await (await request(payloadApiUrl(Collections.Currencies, `all`))).json(), + getWordings: async (): Promise => + await (await request(payloadApiUrl(Collections.Wordings, `all`))).json(), + getPage: async (slug: string): Promise => + await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(), + getCollectible: async (slug: string): Promise => + await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(), + getCollectibleScans: async (slug: string): Promise => + await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}/scans`))).json(), + 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(), + 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(), + getChronologyEventByID: async (id: string): Promise => + await (await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`))).json(), + getImageByID: async (id: string): Promise => + await (await request(payloadApiUrl(Collections.Images, `id/${id}`))).json(), + getAudioByID: async (id: string): Promise => + await (await request(payloadApiUrl(Collections.Audios, `id/${id}`))).json(), + getVideoByID: async (id: string): Promise => + await (await request(payloadApiUrl(Collections.Videos, `id/${id}`))).json(), + getRecorderByID: async (id: string): Promise => + await (await request(payloadApiUrl(Collections.Recorders, `id/${id}`))).json(), + }; }; diff --git a/src/server.ts b/src/server.ts index a8ad7e0..d837d40 100644 --- a/src/server.ts +++ b/src/server.ts @@ -79,7 +79,6 @@ const start = async () => { result.push(removeMockingSection(readFileSync(path.join(__dirname, "constants.ts"), "utf-8"))); result.push("////////////////// SDK //////////////////"); - result.push(`import NodeCache from "node-cache";`); result.push(removeMockingSection(readFileSync(path.join(__dirname, "sdk.ts"), "utf-8"))); res.type("text/plain");