From 6f9d2c0578ac2b47285980d3d8870135ad21b9ec Mon Sep 17 00:00:00 2001 From: DrMint <29893320+DrMint@users.noreply.github.com> Date: Thu, 16 May 2024 15:10:31 +0200 Subject: [PATCH] Moved caching responsability to this repo for SDK use --- TODO.md | 1 - astro.config.ts | 1 + scripts/download-payload-sdk.ts | 2 +- scripts/download-wording-keys.ts | 2 +- .../Topbar/components/CurrencySelector.astro | 2 +- .../Topbar/components/LanguageSelector.astro | 2 +- src/components/Metadata.astro | 2 +- src/i18n/i18n.ts | 2 +- src/middleware.ts | 2 +- .../[locale]/_components/LibraryGrid.astro | 2 +- .../api/hooks/collection-operation.astro | 2 +- src/pages/[locale]/api/pages/partial.astro | 3 +- src/pages/[locale]/api/timeline/partial.astro | 3 +- src/pages/[locale]/audios/[id].astro | 2 +- .../collectibles/[slug]/gallery/[index].astro | 2 +- .../collectibles/[slug]/gallery/index.astro | 3 +- .../[locale]/collectibles/[slug]/index.astro | 3 +- .../collectibles/[slug]/scans/[index].astro | 2 +- .../collectibles/[slug]/scans/index.astro | 2 +- src/pages/[locale]/folders/[slug].astro | 3 +- src/pages/[locale]/images/[id].astro | 2 +- src/pages/[locale]/index.astro | 2 +- src/pages/[locale]/pages/[slug].astro | 2 +- src/pages/[locale]/recorders/[id].astro | 2 +- src/pages/[locale]/settings/index.astro | 2 +- .../timeline/_components/TimelineYear.astro | 2 +- src/pages/[locale]/timeline/index.astro | 4 +- src/pages/[locale]/videos/[id].astro | 2 +- src/shared/payload/payload-sdk.ts | 227 ++++++++---------- src/utils/cachedPayload.ts | 38 --- src/utils/format.ts | 2 +- src/utils/payload.ts | 74 ++++++ 32 files changed, 210 insertions(+), 192 deletions(-) delete mode 100644 src/utils/cachedPayload.ts create mode 100644 src/utils/payload.ts diff --git a/TODO.md b/TODO.md index 7f797a6..4a235a1 100644 --- a/TODO.md +++ b/TODO.md @@ -14,7 +14,6 @@ - [Timeline] Error if collectible not published? - [Timeline] display source language - [Timeline] Add details button in footer with credits + last updated / created -- [SDK] create a initPayload() that return a payload sdk (and stop hard wirring to ENV or node-cache) - [Videos] see why no video on Firefox and no poster on Chrome https://v3.accords-library.com/en/videos/661b672825d380e548dbb8c8 - [Videos] Display platform info + channel page - [Timeline] Handle no JS for footers diff --git a/astro.config.ts b/astro.config.ts index 0236276..198629c 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ }, }), ], + prefetch: false, // devToolbar: { enabled: false }, server: { port: parseInt(ASTRO_PORT ?? "4321"), diff --git a/scripts/download-payload-sdk.ts b/scripts/download-payload-sdk.ts index 1162e07..d262bb3 100644 --- a/scripts/download-payload-sdk.ts +++ b/scripts/download-payload-sdk.ts @@ -7,7 +7,7 @@ const sdk = await fetch(`${import.meta.env.PAYLOAD_API_URL}/sdk`); if (!sdk.ok) { console.error("Failed to get the sdk", sdk.status, sdk.statusText); } else { - const sdkFile = (await sdk.text()).replaceAll("process.env.", "import.meta.env."); + const sdkFile = await sdk.text(); writeFileSync(`${PAYLOAD_FOLDER}/payload-sdk.ts`, sdkFile, { encoding: "utf-8", }); diff --git a/scripts/download-wording-keys.ts b/scripts/download-wording-keys.ts index 0212721..c663a81 100644 --- a/scripts/download-wording-keys.ts +++ b/scripts/download-wording-keys.ts @@ -1,5 +1,5 @@ import { writeFileSync } from "fs"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; const TRANSLATION_FOLDER = `${process.cwd()}/src/i18n`; diff --git a/src/components/AppLayout/components/Topbar/components/CurrencySelector.astro b/src/components/AppLayout/components/Topbar/components/CurrencySelector.astro index 4d7f2b0..37d6077 100644 --- a/src/components/AppLayout/components/Topbar/components/CurrencySelector.astro +++ b/src/components/AppLayout/components/Topbar/components/CurrencySelector.astro @@ -2,7 +2,7 @@ import Button from "components/Button.astro"; import Tooltip from "components/Tooltip.astro"; import { getI18n } from "src/i18n/i18n"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; import { formatCurrency } from "src/utils/currencies"; interface Props { diff --git a/src/components/AppLayout/components/Topbar/components/LanguageSelector.astro b/src/components/AppLayout/components/Topbar/components/LanguageSelector.astro index dd5ae0d..73239b4 100644 --- a/src/components/AppLayout/components/Topbar/components/LanguageSelector.astro +++ b/src/components/AppLayout/components/Topbar/components/LanguageSelector.astro @@ -2,7 +2,7 @@ import Button from "components/Button.astro"; import Tooltip from "components/Tooltip.astro"; import { getI18n } from "src/i18n/i18n"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; import { formatLocale } from "src/utils/format"; interface Props { diff --git a/src/components/Metadata.astro b/src/components/Metadata.astro index 300a293..bfa0dbf 100644 --- a/src/components/Metadata.astro +++ b/src/components/Metadata.astro @@ -1,4 +1,4 @@ - --- +--- import { Icon } from "astro-icon/components"; import type { Attribute } from "src/utils/attributes"; diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 91c3d22..4e5b3d7 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -1,6 +1,6 @@ import type { WordingKey } from "src/i18n/wordings-keys"; import type { ChronologyEvent, EndpointSource } from "src/shared/payload/payload-sdk"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; import { capitalize, formatInlineTitle } from "src/utils/format"; export const defaultLocale = "en"; diff --git a/src/middleware.ts b/src/middleware.ts index 23b9508..314762c 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,5 +1,5 @@ import { defineMiddleware, sequence } from "astro:middleware"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; import acceptLanguage from "accept-language"; import type { AstroCookies } from "astro"; import { z } from "astro:content"; diff --git a/src/pages/[locale]/_components/LibraryGrid.astro b/src/pages/[locale]/_components/LibraryGrid.astro index 89261ec..8220a03 100644 --- a/src/pages/[locale]/_components/LibraryGrid.astro +++ b/src/pages/[locale]/_components/LibraryGrid.astro @@ -1,7 +1,7 @@ --- import CategoryCard from "./CategoryCard.astro"; import { getI18n } from "src/i18n/i18n"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; const { getLocalizedUrl, getLocalizedMatch } = await getI18n(Astro.locals.currentLocale); --- diff --git a/src/pages/[locale]/api/hooks/collection-operation.astro b/src/pages/[locale]/api/hooks/collection-operation.astro index ce69852..4e1b901 100644 --- a/src/pages/[locale]/api/hooks/collection-operation.astro +++ b/src/pages/[locale]/api/hooks/collection-operation.astro @@ -5,7 +5,7 @@ import { refreshLocales, refreshWordings, refreshWebsiteConfig, -} from "src/utils/cachedPayload"; +} from "src/utils/payload"; const auth = Astro.request.headers.get("Authorization"); diff --git a/src/pages/[locale]/api/pages/partial.astro b/src/pages/[locale]/api/pages/partial.astro index db33adb..d084ee1 100644 --- a/src/pages/[locale]/api/pages/partial.astro +++ b/src/pages/[locale]/api/pages/partial.astro @@ -1,6 +1,6 @@ --- import RichText from "components/RichText/RichText.astro"; -import { payload, type EndpointPage } from "src/shared/payload/payload-sdk"; +import type { EndpointPage } from "src/shared/payload/payload-sdk"; import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro"; import MasoTarget from "components/Maso/MasoTarget.astro"; import TableOfContent from "components/TableOfContent/TableOfContent.astro"; @@ -11,6 +11,7 @@ import AsideLayout from "components/AppLayout/AsideLayout.astro"; import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescription.astro"; import Attributes from "components/Attributes.astro"; import type { Attribute } from "src/utils/attributes"; +import { payload } from "src/utils/payload"; export const partial = true; diff --git a/src/pages/[locale]/api/timeline/partial.astro b/src/pages/[locale]/api/timeline/partial.astro index 0db49e6..77b328e 100644 --- a/src/pages/[locale]/api/timeline/partial.astro +++ b/src/pages/[locale]/api/timeline/partial.astro @@ -5,7 +5,8 @@ import TimelineLanguageOverride from "pages/[locale]/timeline/_components/Timeli import TimelineNote from "pages/[locale]/timeline/_components/TimelineNote.astro"; import TimelineSourcesButton from "pages/[locale]/timeline/_components/TimelineSourcesButton.astro"; import { getI18n } from "src/i18n/i18n"; -import { payload, type EndpointChronologyEvent } from "src/shared/payload/payload-sdk"; +import type { EndpointChronologyEvent } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; export const partial = true; diff --git a/src/pages/[locale]/audios/[id].astro b/src/pages/[locale]/audios/[id].astro index e79ef3a..8c4e34c 100644 --- a/src/pages/[locale]/audios/[id].astro +++ b/src/pages/[locale]/audios/[id].astro @@ -7,7 +7,7 @@ import Credits from "components/Credits.astro"; import DownloadButton from "components/DownloadButton.astro"; import RichText from "components/RichText/RichText.astro"; import { getI18n } from "src/i18n/i18n"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import { formatInlineTitle, formatRichTextToString } from "src/utils/format"; import { fetchOr404 } from "src/utils/responses"; diff --git a/src/pages/[locale]/collectibles/[slug]/gallery/[index].astro b/src/pages/[locale]/collectibles/[slug]/gallery/[index].astro index bdda80f..a6353e8 100644 --- a/src/pages/[locale]/collectibles/[slug]/gallery/[index].astro +++ b/src/pages/[locale]/collectibles/[slug]/gallery/[index].astro @@ -2,7 +2,7 @@ import AppLayout from "components/AppLayout/AppLayout.astro"; import Lightbox from "components/Lightbox.astro"; import { getI18n } from "src/i18n/i18n"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import { formatInlineTitle, formatRichTextToString } from "src/utils/format"; import { fetchOr404 } from "src/utils/responses"; diff --git a/src/pages/[locale]/collectibles/[slug]/gallery/index.astro b/src/pages/[locale]/collectibles/[slug]/gallery/index.astro index eaa4280..92fc16e 100644 --- a/src/pages/[locale]/collectibles/[slug]/gallery/index.astro +++ b/src/pages/[locale]/collectibles/[slug]/gallery/index.astro @@ -3,7 +3,7 @@ import AppLayout from "components/AppLayout/AppLayout.astro"; import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescription.astro"; import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro"; import { getI18n } from "src/i18n/i18n"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import { formatInlineTitle, formatRichTextToString } from "src/utils/format"; import { fetchOr404 } from "src/utils/responses"; @@ -75,6 +75,7 @@ const translation = getLocalizedMatch(translations); height: auto; width: auto; box-shadow: 0 5px 20px -10px var(--color-shadow); + border-radius: 16px; } } } diff --git a/src/pages/[locale]/collectibles/[slug]/index.astro b/src/pages/[locale]/collectibles/[slug]/index.astro index 8cf7554..34a6d24 100644 --- a/src/pages/[locale]/collectibles/[slug]/index.astro +++ b/src/pages/[locale]/collectibles/[slug]/index.astro @@ -2,7 +2,7 @@ import AppLayout from "components/AppLayout/AppLayout.astro"; import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro"; import { getI18n } from "src/i18n/i18n"; -import { CollectibleNature, payload } from "src/shared/payload/payload-sdk"; +import { CollectibleNature } from "src/shared/payload/payload-sdk"; import { fetchOr404 } from "src/utils/responses"; import ImageTile from "./_components/ImageTile.astro"; import SizeInfo from "./_components/SizeInfo.astro"; @@ -17,6 +17,7 @@ import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescr import { convert } from "src/utils/currencies"; import Attributes from "components/Attributes.astro"; import type { Attribute } from "src/utils/attributes"; +import { payload } from "src/utils/payload"; const { slug } = Astro.params; const { getLocalizedMatch, getLocalizedUrl, t, formatDate, formatPrice } = await getI18n( diff --git a/src/pages/[locale]/collectibles/[slug]/scans/[index].astro b/src/pages/[locale]/collectibles/[slug]/scans/[index].astro index 8356949..7663829 100644 --- a/src/pages/[locale]/collectibles/[slug]/scans/[index].astro +++ b/src/pages/[locale]/collectibles/[slug]/scans/[index].astro @@ -2,7 +2,7 @@ import AppLayout from "components/AppLayout/AppLayout.astro"; import Lightbox from "components/Lightbox.astro"; import { getI18n } from "src/i18n/i18n"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import { formatInlineTitle, formatRichTextToString } from "src/utils/format"; import { fetchOr404 } from "src/utils/responses"; diff --git a/src/pages/[locale]/collectibles/[slug]/scans/index.astro b/src/pages/[locale]/collectibles/[slug]/scans/index.astro index a8a419d..cf5e6d6 100644 --- a/src/pages/[locale]/collectibles/[slug]/scans/index.astro +++ b/src/pages/[locale]/collectibles/[slug]/scans/index.astro @@ -3,7 +3,7 @@ import AppLayout from "components/AppLayout/AppLayout.astro"; import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro"; import Credits from "components/Credits.astro"; import { getI18n } from "src/i18n/i18n"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import { fetchOr404 } from "src/utils/responses"; import ScanPreview from "./_components/ScanPreview.astro"; import { formatInlineTitle, formatRichTextToString } from "src/utils/format"; diff --git a/src/pages/[locale]/folders/[slug].astro b/src/pages/[locale]/folders/[slug].astro index e12f8f6..94eca7a 100644 --- a/src/pages/[locale]/folders/[slug].astro +++ b/src/pages/[locale]/folders/[slug].astro @@ -1,5 +1,5 @@ --- -import { Collections, payload } from "src/shared/payload/payload-sdk"; +import { Collections } from "src/shared/payload/payload-sdk"; import FoldersSection from "./_components/FoldersSection.astro"; import { fetchOr404 } from "src/utils/responses"; import ErrorMessage from "components/ErrorMessage.astro"; @@ -13,6 +13,7 @@ import VideoPreview from "components/Previews/VideoPreview.astro"; import AppLayout from "components/AppLayout/AppLayout.astro"; import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro"; import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescription.astro"; +import { payload } from "src/utils/payload"; const { slug } = Astro.params; diff --git a/src/pages/[locale]/images/[id].astro b/src/pages/[locale]/images/[id].astro index e29fc22..3fab86d 100644 --- a/src/pages/[locale]/images/[id].astro +++ b/src/pages/[locale]/images/[id].astro @@ -2,7 +2,7 @@ import AppLayout from "components/AppLayout/AppLayout.astro"; import Lightbox from "components/Lightbox.astro"; import { getI18n } from "src/i18n/i18n"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import { formatInlineTitle, formatRichTextToString } from "src/utils/format"; import { fetchOr404 } from "src/utils/responses"; diff --git a/src/pages/[locale]/index.astro b/src/pages/[locale]/index.astro index 4859b30..9d9d4bb 100644 --- a/src/pages/[locale]/index.astro +++ b/src/pages/[locale]/index.astro @@ -5,7 +5,7 @@ import LibraryGrid from "./_components/LibraryGrid.astro"; import ChronicleCard from "./_components/ChronicleCard.astro"; import LinkCard from "./_components/LinkCard.astro"; import { getI18n } from "src/i18n/i18n"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; import AppLayout from "components/AppLayout/AppLayout.astro"; import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescription.astro"; diff --git a/src/pages/[locale]/pages/[slug].astro b/src/pages/[locale]/pages/[slug].astro index 76d0d95..cb6f00a 100644 --- a/src/pages/[locale]/pages/[slug].astro +++ b/src/pages/[locale]/pages/[slug].astro @@ -2,7 +2,7 @@ import AppLayout from "components/AppLayout/AppLayout.astro"; import { getI18n } from "src/i18n/i18n"; import Page from "src/pages/[locale]/api/pages/partial.astro"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import { formatInlineTitle, formatRichTextToString } from "src/utils/format"; import { fetchOr404 } from "src/utils/responses"; diff --git a/src/pages/[locale]/recorders/[id].astro b/src/pages/[locale]/recorders/[id].astro index 4ebafcb..f2b1123 100644 --- a/src/pages/[locale]/recorders/[id].astro +++ b/src/pages/[locale]/recorders/[id].astro @@ -5,7 +5,7 @@ import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro import Attributes from "components/Attributes.astro"; import RichText from "components/RichText/RichText.astro"; import { getI18n } from "src/i18n/i18n"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import type { Attribute } from "src/utils/attributes"; import { formatLocale } from "src/utils/format"; import { fetchOr404 } from "src/utils/responses"; diff --git a/src/pages/[locale]/settings/index.astro b/src/pages/[locale]/settings/index.astro index 315356d..198abc4 100644 --- a/src/pages/[locale]/settings/index.astro +++ b/src/pages/[locale]/settings/index.astro @@ -2,7 +2,7 @@ import AppLayout from "components/AppLayout/AppLayout.astro"; import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro"; import { getI18n } from "src/i18n/i18n"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; import { formatCurrency } from "src/utils/currencies"; import { formatLocale } from "src/utils/format"; diff --git a/src/pages/[locale]/timeline/_components/TimelineYear.astro b/src/pages/[locale]/timeline/_components/TimelineYear.astro index 7b2cb50..b2d0a36 100644 --- a/src/pages/[locale]/timeline/_components/TimelineYear.astro +++ b/src/pages/[locale]/timeline/_components/TimelineYear.astro @@ -2,7 +2,7 @@ import type { EndpointChronologyEvent } from "src/shared/payload/payload-sdk"; import TimelineEvent from "./TimelineEvent.astro"; import { getI18n } from "src/i18n/i18n"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; interface Props { year: number; diff --git a/src/pages/[locale]/timeline/index.astro b/src/pages/[locale]/timeline/index.astro index a901cc7..291afdd 100644 --- a/src/pages/[locale]/timeline/index.astro +++ b/src/pages/[locale]/timeline/index.astro @@ -1,5 +1,5 @@ --- -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import { groupBy } from "src/utils/array"; import TimelineYear from "./_components/TimelineYear.astro"; import AppLayout from "components/AppLayout/AppLayout.astro"; @@ -7,7 +7,7 @@ import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro import Card from "components/Card.astro"; import { getI18n } from "src/i18n/i18n"; import AppLayoutBackgroundImg from "components/AppLayout/components/AppLayoutBackgroundImg.astro"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; import type { WordingKey } from "src/i18n/wordings-keys"; import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescription.astro"; diff --git a/src/pages/[locale]/videos/[id].astro b/src/pages/[locale]/videos/[id].astro index 81619d2..af60027 100644 --- a/src/pages/[locale]/videos/[id].astro +++ b/src/pages/[locale]/videos/[id].astro @@ -7,7 +7,7 @@ import DownloadButton from "components/DownloadButton.astro"; import RichText from "components/RichText/RichText.astro"; import VideoPlayer from "components/VideoPlayer.astro"; import { getI18n } from "src/i18n/i18n"; -import { payload } from "src/shared/payload/payload-sdk"; +import { payload } from "src/utils/payload"; import { formatInlineTitle, formatRichTextToString } from "src/utils/format"; import { fetchOr404 } from "src/utils/responses"; diff --git a/src/shared/payload/payload-sdk.ts b/src/shared/payload/payload-sdk.ts index b8a0677..caf1699 100644 --- a/src/shared/payload/payload-sdk.ts +++ b/src/shared/payload/payload-sdk.ts @@ -1349,86 +1349,6 @@ export const isBlockLineBlock = (block: GenericBlock): block is LineBlock => ////////////////// SDK ////////////////// -import NodeCache from "node-cache"; - - -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: import.meta.env.PAYLOAD_USER, - password: import.meta.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 => - `${import.meta.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; @@ -1868,49 +1788,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/utils/cachedPayload.ts b/src/utils/cachedPayload.ts deleted file mode 100644 index fa10255..0000000 --- a/src/utils/cachedPayload.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - payload, - type Language, - type EndpointWording, - type EndpointWebsiteConfig, -} from "src/shared/payload/payload-sdk"; - -type Cache = { - locales: Language[]; - currencies: string[]; - wordings: EndpointWording[]; - config: EndpointWebsiteConfig; -}; - -const fetchNewData = async (): Promise => ({ - locales: await payload.getLanguages(), - currencies: (await payload.getCurrencies()).map(({ id }) => id), - wordings: await payload.getWordings(), - config: await payload.getConfig(), -}); - -export let cache = await fetchNewData(); - -export const refreshWordings = async () => { - cache.wordings = await payload.getWordings(); -}; - -export const refreshCurrencies = async () => { - cache.currencies = (await payload.getCurrencies()).map(({ id }) => id); -}; - -export const refreshLocales = async () => { - cache.locales = await payload.getLanguages(); -}; - -export const refreshWebsiteConfig = async () => { - cache.config = await payload.getConfig(); -}; diff --git a/src/utils/format.ts b/src/utils/format.ts index 894dcb1..5c8eaa8 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -6,7 +6,7 @@ import { type RichTextContent, type RichTextNode, } from "src/shared/payload/payload-sdk"; -import { cache } from "src/utils/cachedPayload"; +import { cache } from "src/utils/payload"; export const formatLocale = (code: string): string => cache.locales.find(({ id }) => id === code)?.name ?? code; diff --git a/src/utils/payload.ts b/src/utils/payload.ts new file mode 100644 index 0000000..fbc2471 --- /dev/null +++ b/src/utils/payload.ts @@ -0,0 +1,74 @@ +import { + type Language, + type EndpointWording, + 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"; + +export const payload = getPayloadSDK({ + apiURL: import.meta.env.PAYLOAD_API_URL, + email: import.meta.env.PAYLOAD_USER, + password: import.meta.env.PAYLOAD_PASSWORD, + cache: { + 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; + } + console.log("No token to be retrieved or the token expired"); + return undefined; + }, + 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); + }, + }, +}); + +type Cache = { + locales: Language[]; + currencies: string[]; + wordings: EndpointWording[]; + config: EndpointWebsiteConfig; +}; + +const fetchNewData = async (): Promise => ({ + locales: await payload.getLanguages(), + currencies: (await payload.getCurrencies()).map(({ id }) => id), + wordings: await payload.getWordings(), + config: await payload.getConfig(), +}); + +export let cache = await fetchNewData(); + +export const refreshWordings = async () => { + cache.wordings = await payload.getWordings(); +}; + +export const refreshCurrencies = async () => { + cache.currencies = (await payload.getCurrencies()).map(({ id }) => id); +}; + +export const refreshLocales = async () => { + cache.locales = await payload.getLanguages(); +}; + +export const refreshWebsiteConfig = async () => { + cache.config = await payload.getConfig(); +};