Added page caching
This commit is contained in:
parent
de9ad38835
commit
cbbf0e5e3b
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "v3.accords-library.com",
|
||||
"version": "3.0.0-beta.6",
|
||||
"version": "3.0.0-beta.7",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import { writeFileSync } from "fs";
|
||||
import { payload } from "src/utils/payload";
|
||||
import { PayloadSDK } from "src/shared/payload/payload-sdk";
|
||||
|
||||
const TRANSLATION_FOLDER = `${process.cwd()}/src/i18n`;
|
||||
|
||||
const payload = new PayloadSDK(
|
||||
import.meta.env.PAYLOAD_API_URL,
|
||||
import.meta.env.PAYLOAD_USER,
|
||||
import.meta.env.PAYLOAD_PASSWORD
|
||||
);
|
||||
|
||||
try {
|
||||
const wordings = await payload.getWordings();
|
||||
const { data: wordings } = await payload.getWordings();
|
||||
const keys = wordings.map(({ name }) => name);
|
||||
|
||||
let result = "";
|
||||
|
|
|
@ -2,14 +2,16 @@ import type {
|
|||
EndpointWebsiteConfig,
|
||||
EndpointWording,
|
||||
Language,
|
||||
PayloadSDK,
|
||||
} from "src/shared/payload/payload-sdk";
|
||||
import { getLogger } from "src/utils/logger";
|
||||
import { payload } from "src/utils/payload";
|
||||
|
||||
class ContextCache {
|
||||
export class ContextCache {
|
||||
private initialized = false;
|
||||
private logger = getLogger("[ContextCache]");
|
||||
|
||||
constructor(private readonly payload: PayloadSDK) {}
|
||||
|
||||
locales: Language[] = [];
|
||||
currencies: string[] = [];
|
||||
wordings: EndpointWording[] = [];
|
||||
|
@ -33,26 +35,22 @@ class ContextCache {
|
|||
}
|
||||
|
||||
async refreshWordings() {
|
||||
this.wordings = await payload.getWordings();
|
||||
this.wordings = (await this.payload.getWordings()).data;
|
||||
this.logger.log("Wordings refreshed");
|
||||
}
|
||||
|
||||
async refreshCurrencies() {
|
||||
contextCache.currencies = (await payload.getCurrencies()).map(({ id }) => id);
|
||||
this.currencies = (await this.payload.getCurrencies()).data.map(({ id }) => id);
|
||||
this.logger.log("Currencies refreshed");
|
||||
}
|
||||
|
||||
async refreshLocales() {
|
||||
contextCache.locales = await payload.getLanguages();
|
||||
this.locales = (await this.payload.getLanguages()).data;
|
||||
this.logger.log("Locales refreshed");
|
||||
}
|
||||
|
||||
async refreshWebsiteConfig() {
|
||||
contextCache.config = await payload.getConfig();
|
||||
this.config = (await this.payload.getConfig()).data;
|
||||
this.logger.log("WebsiteConfig refreshed");
|
||||
}
|
||||
}
|
||||
|
||||
const contextCache = new ContextCache();
|
||||
await contextCache.init();
|
||||
export { contextCache };
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
import type { PayloadSDK } from "src/shared/payload/payload-sdk";
|
||||
import { getLogger } from "src/utils/logger";
|
||||
import { payload } from "src/utils/payload";
|
||||
|
||||
class DataCache {
|
||||
export class DataCache {
|
||||
private readonly logger = getLogger("[DataCache]");
|
||||
private initialized = false;
|
||||
|
||||
private readonly responseCache = new Map<string, any>();
|
||||
private readonly idsCacheMap = new Map<string, Set<string>>();
|
||||
|
||||
constructor(
|
||||
private readonly payload: PayloadSDK,
|
||||
private readonly onInvalidate: (urls: string[]) => Promise<void>
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
if (this.initialized) return;
|
||||
|
||||
|
@ -19,10 +24,10 @@ class DataCache {
|
|||
}
|
||||
|
||||
private async precache() {
|
||||
const { urls } = await payload.getAllSdkUrls();
|
||||
for (const url of urls) {
|
||||
const { data } = await this.payload.getAllSdkUrls();
|
||||
for (const url of data.urls) {
|
||||
try {
|
||||
await payload.request(url);
|
||||
await this.payload.request(url);
|
||||
} catch {
|
||||
this.logger.warn("Precaching failed for url", url);
|
||||
}
|
||||
|
@ -71,14 +76,13 @@ class DataCache {
|
|||
this.responseCache.delete(url);
|
||||
this.logger.log("Invalidated cache for", url);
|
||||
try {
|
||||
await payload.request(url);
|
||||
await this.payload.request(url);
|
||||
} catch (e) {
|
||||
this.logger.log("Revalidation fails for", url);
|
||||
}
|
||||
}
|
||||
|
||||
this.onInvalidate([...urlsToInvalidate]);
|
||||
this.logger.log("There are currently", this.responseCache.size, "responses in cache.");
|
||||
}
|
||||
}
|
||||
|
||||
export const dataCache = new DataCache();
|
||||
|
|
|
@ -1,11 +1,66 @@
|
|||
import type { PayloadSDK } from "src/shared/payload/payload-sdk";
|
||||
import { getLogger } from "src/utils/logger";
|
||||
|
||||
class PageCache {
|
||||
export class PageCache {
|
||||
private readonly logger = getLogger("[PageCache]");
|
||||
private readonly cache = new Map<string, Response>();
|
||||
private initialized = false;
|
||||
|
||||
private readonly responseCache = new Map<string, Response>();
|
||||
private readonly invalidationMap = new Map<string, Set<string>>();
|
||||
|
||||
constructor(private readonly payload: PayloadSDK) {}
|
||||
|
||||
async init() {
|
||||
if (this.initialized) return;
|
||||
|
||||
if (import.meta.env.ENABLE_PRECACHING === "true") {
|
||||
await this.precacheAll();
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
private async precacheAll() {
|
||||
const { data: languages } = await this.payload.getLanguages();
|
||||
const locales = languages.map(({ id }) => id);
|
||||
|
||||
await this.precache("/", locales);
|
||||
await this.precache("/settings", locales);
|
||||
await this.precache("/timeline", locales);
|
||||
|
||||
const { data: folders } = await this.payload.getFolderSlugs();
|
||||
for (const slug of folders) {
|
||||
await this.precache(`/folders/${slug}`, locales);
|
||||
}
|
||||
|
||||
const { data: pages } = await this.payload.getPageSlugs();
|
||||
for (const slug of pages) {
|
||||
await this.precache(`/pages/${slug}`, locales);
|
||||
}
|
||||
|
||||
const { data: collectibles } = await this.payload.getCollectibleSlugs();
|
||||
for (const slug of collectibles) {
|
||||
await this.precache(`/collectibles/${slug}`, locales);
|
||||
}
|
||||
|
||||
this.logger.log("Precaching completed!", this.responseCache.size, "responses cached");
|
||||
}
|
||||
|
||||
private async precache(pathname: string, locales: string[]) {
|
||||
try {
|
||||
await Promise.all(
|
||||
locales.map((locale) => {
|
||||
const url = `http://${import.meta.env.ASTRO_HOST}:${import.meta.env.ASTRO_PORT}/${locale}${pathname}`;
|
||||
return fetch(url);
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.warn("Precaching failed for page", pathname);
|
||||
}
|
||||
}
|
||||
|
||||
get(url: string): Response | undefined {
|
||||
const cachedPage = this.cache.get(url);
|
||||
const cachedPage = this.responseCache.get(url);
|
||||
if (cachedPage) {
|
||||
this.logger.log("Retrieved cached page for", url);
|
||||
return cachedPage?.clone();
|
||||
|
@ -13,10 +68,40 @@ class PageCache {
|
|||
return;
|
||||
}
|
||||
|
||||
set(url: string, response: Response) {
|
||||
this.cache.set(url, response.clone());
|
||||
set(url: string, response: Response, sdkCalls: string[]) {
|
||||
sdkCalls.forEach((id) => {
|
||||
const current = this.invalidationMap.get(id);
|
||||
if (current) {
|
||||
current.add(url);
|
||||
} else {
|
||||
this.invalidationMap.set(id, new Set([url]));
|
||||
}
|
||||
});
|
||||
|
||||
this.responseCache.set(url, response.clone());
|
||||
this.logger.log("Cached response for", url);
|
||||
}
|
||||
}
|
||||
|
||||
export const pageCache = new PageCache();
|
||||
async invalidate(sdkUrls: string[]) {
|
||||
const pagesToInvalidate = new Set<string>();
|
||||
|
||||
sdkUrls.forEach((url) => {
|
||||
const pagesForThisSDKUrl = this.invalidationMap.get(url);
|
||||
if (!pagesForThisSDKUrl) return;
|
||||
this.invalidationMap.delete(url);
|
||||
[...pagesForThisSDKUrl].forEach((page) => pagesToInvalidate.add(page));
|
||||
});
|
||||
|
||||
for (const url of pagesToInvalidate) {
|
||||
this.responseCache.delete(url);
|
||||
this.logger.log("Invalidated cache for", url);
|
||||
try {
|
||||
await fetch(`http://${import.meta.env.ASTRO_HOST}:${import.meta.env.ASTRO_PORT}${url}`);
|
||||
} catch (e) {
|
||||
this.logger.log("Revalidation fails for", url);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log("There are currently", this.responseCache.size, "responses in cache.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
export class TokenCache {
|
||||
private token: string | undefined;
|
||||
private expiration: number | undefined;
|
||||
|
||||
get() {
|
||||
if (!this.token) return undefined;
|
||||
if (!this.expiration || this.expiration < Date.now()) {
|
||||
console.log("[PayloadSDK] No token to be retrieved or the token expired");
|
||||
return undefined;
|
||||
}
|
||||
return this.token;
|
||||
}
|
||||
|
||||
set(newToken: string, newExpiration: number) {
|
||||
this.token = newToken;
|
||||
this.expiration = newExpiration * 1000;
|
||||
const diffInMinutes = Math.floor((this.expiration - Date.now()) / 1000 / 60);
|
||||
console.log("[PayloadSDK] New token set. TTL is", diffInMinutes, "minutes.");
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
import Html from "./components/Html.astro";
|
||||
import Topbar from "./components/Topbar/Topbar.astro";
|
||||
import Footer from "./components/Footer.astro";
|
||||
import type { EndpointSource } from "src/shared/payload/payload-sdk";
|
||||
import { getSDKEndpoint, type EndpointSource } from "src/shared/payload/payload-sdk";
|
||||
import AppLayoutBackgroundImg from "./components/AppLayoutBackgroundImg.astro";
|
||||
import type { ComponentProps } from "astro/types";
|
||||
|
||||
|
@ -15,6 +15,10 @@ interface Props {
|
|||
class?: string | undefined;
|
||||
}
|
||||
|
||||
Astro.locals.sdkCalls.add(getSDKEndpoint.getCurrenciesEndpoint());
|
||||
Astro.locals.sdkCalls.add(getSDKEndpoint.getLanguagesEndpoint());
|
||||
Astro.locals.sdkCalls.add(getSDKEndpoint.getWordingsEndpoint());
|
||||
|
||||
const {
|
||||
openGraph,
|
||||
parentPages,
|
||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
|||
EndpointPayloadImage,
|
||||
EndpointVideo,
|
||||
} from "src/shared/payload/payload-sdk";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
import { PostProcessingTags } from "src/middleware/postProcessing";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import Button from "components/Button.astro";
|
||||
import Tooltip from "components/Tooltip.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
import { formatCurrency } from "src/utils/currencies";
|
||||
import {
|
||||
PostProcessingTags,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import Button from "components/Button.astro";
|
||||
import Tooltip from "components/Tooltip.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
import { formatLocale } from "src/utils/format";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -5,5 +5,6 @@ declare namespace App {
|
|||
interface Locals {
|
||||
currentLocale: string;
|
||||
notFound: boolean;
|
||||
sdkCalls: Set<string>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { WordingKey } from "src/i18n/wordings-keys";
|
||||
import type { ChronologyEvent, EndpointSource } from "src/shared/payload/payload-sdk";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
import { capitalize, formatInlineTitle } from "src/utils/format";
|
||||
|
||||
export const defaultLocale = "en";
|
||||
|
|
|
@ -8,8 +8,6 @@ import { requestTrackingMiddleware } from "src/middleware/requestTracking";
|
|||
import { pageCachingMiddleware } from "src/middleware/pageCaching";
|
||||
import { setAstroLocalsMiddleware } from "src/middleware/setAstroLocals";
|
||||
|
||||
|
||||
|
||||
export const onRequest = sequence(
|
||||
// Possible redirect
|
||||
actionsHandlingMiddleware,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { defineMiddleware } from "astro:middleware";
|
||||
import { pageCache } from "src/cache/pageCache";
|
||||
import { pageCache } from "src/utils/payload";
|
||||
|
||||
export const pageCachingMiddleware = defineMiddleware(async ({ url, request }, next) => {
|
||||
const blacklist = ["/en/api/hooks/collection-operation", "/en/api/on-startup"];
|
||||
|
||||
export const pageCachingMiddleware = defineMiddleware(async ({ url, request, locals }, next) => {
|
||||
const pathname = url.pathname;
|
||||
const cachedPage = pageCache.get(pathname);
|
||||
|
||||
|
@ -20,7 +22,10 @@ export const pageCachingMiddleware = defineMiddleware(async ({ url, request }, n
|
|||
|
||||
if (response.ok) {
|
||||
response.headers.set("Last-Modified", new Date().toUTCString());
|
||||
pageCache.set(pathname, response);
|
||||
|
||||
if (!blacklist.includes(pathname)) {
|
||||
pageCache.set(pathname, response, [...locals.sdkCalls]);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
|
|
|
@ -53,8 +53,6 @@ export const postProcessingMiddleware = defineMiddleware(async ({ cookies, local
|
|||
|
||||
let html = await response.text();
|
||||
|
||||
const t0 = performance.now();
|
||||
|
||||
// HTML CLASS
|
||||
const currentTheme = getCookieTheme(cookies) ?? "auto";
|
||||
html = html.replace(
|
||||
|
@ -102,7 +100,5 @@ export const postProcessingMiddleware = defineMiddleware(async ({ cookies, local
|
|||
}
|
||||
});
|
||||
|
||||
console.log("Post-processing:", performance.now() - t0, "ms");
|
||||
|
||||
return new Response(html, response);
|
||||
});
|
||||
|
|
|
@ -3,5 +3,6 @@ import { getCurrentLocale } from "src/middleware/utils";
|
|||
|
||||
export const setAstroLocalsMiddleware = defineMiddleware(async ({ url, locals }, next) => {
|
||||
locals.currentLocale = getCurrentLocale(url.pathname) ?? "en";
|
||||
locals.sdkCalls = new Set();
|
||||
return next();
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
import type { AstroCookies } from "astro";
|
||||
import { z } from "astro:content";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
import acceptLanguage from "accept-language";
|
||||
|
||||
export const getAbsoluteLocaleUrl = (locale: string, url: string) => `/${locale}${url}`;
|
||||
|
@ -26,7 +26,7 @@ export const getCurrentLocale = (pathname: string): string | undefined => {
|
|||
|
||||
export const getBestAcceptedLanguage = (request: Request): string | undefined => {
|
||||
const header = request.headers.get("Accept-Language");
|
||||
if (!header) return;
|
||||
if (!header || header === "*") return;
|
||||
|
||||
acceptLanguage.languages(contextCache.locales.map(({ id }) => id));
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
import LibraryCard from "./LibraryCard.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
|
||||
const { getLocalizedUrl, getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
---
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { dataCache } from "src/cache/dataCache";
|
||||
import { contextCache, dataCache } from "src/utils/payload.ts";
|
||||
import { Collections, type AfterOperationWebHookMessage } from "src/shared/payload/payload-sdk";
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import { dataCache } from "src/cache/dataCache";
|
||||
import { contextCache, dataCache, pageCache } from "src/utils/payload";
|
||||
|
||||
export const GET: APIRoute = async () => {
|
||||
await contextCache.init();
|
||||
await dataCache.init();
|
||||
await pageCache.init();
|
||||
return new Response(null, { status: 200, statusText: "Ok" });
|
||||
};
|
||||
|
|
|
@ -24,8 +24,14 @@ interface Props {
|
|||
const reqUrl = new URL(Astro.request.url);
|
||||
const lang = Astro.props.lang ?? reqUrl.searchParams.get("lang")!;
|
||||
const slug = Astro.props.slug ?? reqUrl.searchParams.get("slug")!;
|
||||
|
||||
const { translations, thumbnail, createdAt, updatedAt, updatedBy, attributes } =
|
||||
Astro.props.page ?? (await payload.getPage(slug));
|
||||
await (async () => {
|
||||
if (Astro.props.page) return Astro.props.page;
|
||||
const response = await payload.getPage(slug);
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
return response.data;
|
||||
})();
|
||||
|
||||
const { getLocalizedUrl, t, formatDate } = await getI18n(Astro.locals.currentLocale);
|
||||
const { getLocalizedMatch } = await getI18n(lang);
|
||||
|
|
|
@ -12,17 +12,22 @@ import { payload } from "src/utils/payload";
|
|||
export const partial = true;
|
||||
|
||||
interface Props {
|
||||
lang: string;
|
||||
event: EndpointChronologyEvent;
|
||||
id: string;
|
||||
index: number;
|
||||
lang?: string;
|
||||
event?: EndpointChronologyEvent;
|
||||
id?: string;
|
||||
index?: number;
|
||||
}
|
||||
|
||||
const reqUrl = new URL(Astro.request.url);
|
||||
const lang = Astro.props.lang ?? reqUrl.searchParams.get("lang")!;
|
||||
const id = Astro.props.id ?? reqUrl.searchParams.get("id")!;
|
||||
const index = Astro.props.index ?? parseInt(reqUrl.searchParams.get("index")!);
|
||||
const event = Astro.props.event ?? (await payload.getChronologyEventByID(id));
|
||||
const event = await (async () => {
|
||||
if (Astro.props.event) return Astro.props.event;
|
||||
const response = await payload.getChronologyEventByID(id);
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
return response.data;
|
||||
})();
|
||||
const { sources, translations } = event.events[index]!;
|
||||
|
||||
const { getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
|
||||
|
|
|
@ -13,10 +13,12 @@ import { fetchOr404 } from "src/utils/responses";
|
|||
import { getFileIcon } from "src/utils/attributes";
|
||||
|
||||
const id = Astro.params.id!;
|
||||
const audio = await fetchOr404(() => payload.getAudioByID(id));
|
||||
if (audio instanceof Response) {
|
||||
return audio;
|
||||
const response = await fetchOr404(() => payload.getAudioByID(id));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const audio = response.data;
|
||||
|
||||
const { getLocalizedMatch, t, formatFilesize, formatDate } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
|
|
|
@ -13,12 +13,13 @@ const { getLocalizedUrl, getLocalizedMatch, t, formatDate } = await getI18n(
|
|||
Astro.locals.currentLocale
|
||||
);
|
||||
|
||||
const galleryImage = await fetchOr404(() => payload.getCollectibleGalleryImage(slug, index));
|
||||
if (galleryImage instanceof Response) {
|
||||
return galleryImage;
|
||||
const response = await fetchOr404(() => payload.getCollectibleGalleryImage(slug, index));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const { parentPages, previousIndex, nextIndex, image } = response.data;
|
||||
|
||||
const { parentPages, previousIndex, nextIndex, image } = galleryImage;
|
||||
const { filename, translations, createdAt, updatedAt, credits, attributes, mimeType } = image;
|
||||
|
||||
const { pretitle, title, subtitle, description, language } =
|
||||
|
|
|
@ -11,12 +11,13 @@ import RichText from "components/RichText/RichText.astro";
|
|||
const slug = Astro.params.slug!;
|
||||
const { getLocalizedMatch, getLocalizedUrl, t } = await getI18n(Astro.locals.currentLocale);
|
||||
|
||||
const gallery = await fetchOr404(() => payload.getCollectibleGallery(slug));
|
||||
if (gallery instanceof Response) {
|
||||
return gallery;
|
||||
const response = await fetchOr404(() => payload.getCollectibleGallery(slug));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const { translations, parentPages, images, thumbnail } = response.data;
|
||||
|
||||
const { translations, parentPages, images, thumbnail } = gallery;
|
||||
const translation = getLocalizedMatch(translations);
|
||||
---
|
||||
|
||||
|
|
|
@ -26,10 +26,12 @@ const { getLocalizedMatch, getLocalizedUrl, t, formatDate } = await getI18n(
|
|||
Astro.locals.currentLocale
|
||||
);
|
||||
|
||||
const collectible = await fetchOr404(() => payload.getCollectible(slug));
|
||||
if (collectible instanceof Response) {
|
||||
return collectible;
|
||||
const response = await fetchOr404(() => payload.getCollectible(slug));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const collectible = response.data;
|
||||
|
||||
const {
|
||||
translations,
|
||||
|
|
|
@ -12,12 +12,13 @@ const { formatScanIndexShort, getLocalizedMatch, getLocalizedUrl } = await getI1
|
|||
Astro.locals.currentLocale
|
||||
);
|
||||
|
||||
const scanPage = await fetchOr404(() => payload.getCollectibleScanPage(slug, index));
|
||||
if (scanPage instanceof Response) {
|
||||
return scanPage;
|
||||
const response = await fetchOr404(() => payload.getCollectibleScanPage(slug, index));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const { parentPages, previousIndex, nextIndex, image, translations } = response.data;
|
||||
|
||||
const { parentPages, previousIndex, nextIndex, image, translations } = scanPage;
|
||||
const translation = getLocalizedMatch(translations);
|
||||
---
|
||||
|
||||
|
|
|
@ -12,12 +12,13 @@ import RichText from "components/RichText/RichText.astro";
|
|||
const slug = Astro.params.slug!;
|
||||
const { getLocalizedMatch, t } = await getI18n(Astro.locals.currentLocale);
|
||||
|
||||
const scans = await fetchOr404(() => payload.getCollectibleScans(slug));
|
||||
if (scans instanceof Response) {
|
||||
return scans;
|
||||
const response = await fetchOr404(() => payload.getCollectibleScans(slug));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const { translations, credits, cover, pages, dustjacket, obi, parentPages, thumbnail } = scans;
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const { translations, credits, cover, pages, dustjacket, obi, parentPages, thumbnail } =
|
||||
response.data;
|
||||
|
||||
const translation = getLocalizedMatch(translations);
|
||||
|
||||
|
|
|
@ -14,14 +14,11 @@ import { Icon } from "astro-icon/components";
|
|||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
const id = Astro.params.id!;
|
||||
const video = await fetchOr404(() => payload.getFileByID(id));
|
||||
if (video instanceof Response) {
|
||||
return video;
|
||||
const response = await fetchOr404(() => payload.getFileByID(id));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const { getLocalizedMatch, t, formatFilesize, formatDate } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const {
|
||||
translations,
|
||||
attributes,
|
||||
|
@ -33,7 +30,11 @@ const {
|
|||
updatedAt,
|
||||
createdAt,
|
||||
thumbnail,
|
||||
} = video;
|
||||
} = response.data;
|
||||
|
||||
const { getLocalizedMatch, t, formatFilesize, formatDate } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
|
||||
const { pretitle, title, subtitle, description, language } =
|
||||
translations.length > 0
|
||||
|
|
|
@ -18,36 +18,38 @@ import FilePreview from "components/Previews/FilePreview.astro";
|
|||
|
||||
const slug = Astro.params.slug!;
|
||||
|
||||
const folder = await fetchOr404(() => payload.getFolder(slug));
|
||||
if (folder instanceof Response) {
|
||||
return folder;
|
||||
const response = await fetchOr404(() => payload.getFolder(slug));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const { files, parentPages, sections, translations } = response.data;
|
||||
|
||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
const meta = getLocalizedMatch(folder.translations);
|
||||
const { language, title, description } = getLocalizedMatch(translations);
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<AppLayout
|
||||
openGraph={{
|
||||
title: meta.title,
|
||||
description: meta.description && formatRichTextToString(meta.description),
|
||||
title: title,
|
||||
description: description && formatRichTextToString(description),
|
||||
}}
|
||||
parentPages={folder.parentPages}
|
||||
parentPages={parentPages}
|
||||
class="app">
|
||||
<AppLayoutTitle title={meta.title} lang={meta.language} />
|
||||
{meta.description && <RichText content={meta.description} context={{ lang: meta.language }} />}
|
||||
<AppLayoutTitle title={title} lang={language} />
|
||||
{description && <RichText content={description} context={{ lang: language }} />}
|
||||
|
||||
<div id="main" class:list={{ complex: folder.sections.type === "multiple" }}>
|
||||
<div id="main" class:list={{ complex: sections.type === "multiple" }}>
|
||||
{
|
||||
folder.sections.type === "single" && folder.sections.subfolders.length > 0 ? (
|
||||
<FoldersSection folders={folder.sections.subfolders} />
|
||||
sections.type === "single" && sections.subfolders.length > 0 ? (
|
||||
<FoldersSection folders={sections.subfolders} />
|
||||
) : (
|
||||
folder.sections.type === "multiple" &&
|
||||
folder.sections.sections.length > 0 && (
|
||||
sections.type === "multiple" &&
|
||||
sections.sections.length > 0 && (
|
||||
<div id="sections">
|
||||
{folder.sections.sections.map(({ subfolders, translations }) => {
|
||||
{sections.sections.map(({ subfolders, translations }) => {
|
||||
const { language, name } = getLocalizedMatch(translations);
|
||||
return <FoldersSection folders={subfolders} title={name} lang={language} />;
|
||||
})}
|
||||
|
@ -58,7 +60,7 @@ const meta = getLocalizedMatch(folder.translations);
|
|||
|
||||
<div id="files">
|
||||
{
|
||||
folder.files.map(({ relationTo, value }) => {
|
||||
files.map(({ relationTo, value }) => {
|
||||
switch (relationTo) {
|
||||
case Collections.Collectibles:
|
||||
return <CollectiblePreview collectible={value} />;
|
||||
|
|
|
@ -8,13 +8,15 @@ import { fetchOr404 } from "src/utils/responses";
|
|||
import { getFileIcon } from "src/utils/attributes";
|
||||
|
||||
const id = Astro.params.id!;
|
||||
const image = await fetchOr404(() => payload.getImageByID(id));
|
||||
if (image instanceof Response) {
|
||||
return image;
|
||||
const response = await fetchOr404(() => payload.getImageByID(id));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const image = response.data;
|
||||
const { filename, translations, attributes, credits, createdAt, updatedAt, mimeType } = image;
|
||||
|
||||
const { getLocalizedMatch, formatDate, t } = await getI18n(Astro.locals.currentLocale);
|
||||
const { filename, translations, attributes, credits, createdAt, updatedAt, mimeType } = image;
|
||||
|
||||
const { pretitle, title, subtitle, description, language } =
|
||||
translations.length > 0
|
||||
|
|
|
@ -3,7 +3,7 @@ import Button from "components/Button.astro";
|
|||
import LibraryGrid from "./_components/LibraryGrid.astro";
|
||||
import LinkCard from "./_components/LinkCard.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
import AppLayout from "components/AppLayout/AppLayout.astro";
|
||||
import HomeTitle from "./_components/HomeTitle.astro";
|
||||
|
||||
|
|
|
@ -8,11 +8,12 @@ import { fetchOr404 } from "src/utils/responses";
|
|||
|
||||
const slug = Astro.params.slug!;
|
||||
|
||||
const page = await fetchOr404(() => payload.getPage(slug));
|
||||
if (page instanceof Response) {
|
||||
return page;
|
||||
const response = await fetchOr404(() => payload.getPage(slug));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const page = response.data;
|
||||
const { parentPages, thumbnail, translations, backgroundImage } = page;
|
||||
|
||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
|
|
|
@ -12,15 +12,15 @@ import { fetchOr404 } from "src/utils/responses";
|
|||
import { sizesToSrcset } from "src/utils/img";
|
||||
|
||||
const id = Astro.params.id!;
|
||||
const recorder = await fetchOr404(() => payload.getRecorderByID(id));
|
||||
if (recorder instanceof Response) {
|
||||
return recorder;
|
||||
const response = await fetchOr404(() => payload.getRecorderByID(id));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const { username, languages, avatar, translations } = response.data;
|
||||
|
||||
const { t, getLocalizedMatch, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
|
||||
|
||||
const { username, languages, avatar, translations } = recorder;
|
||||
|
||||
const { biography, language } =
|
||||
translations.length > 0
|
||||
? getLocalizedMatch(translations)
|
||||
|
|
|
@ -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 { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
import { formatCurrency } from "src/utils/currencies";
|
||||
import { formatLocale } from "src/utils/format";
|
||||
import { prepareClassForSelectedCurrencyPostProcessing } from "src/middleware/postProcessing";
|
||||
|
|
|
@ -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 { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
|
||||
interface Props {
|
||||
year: number;
|
||||
|
@ -17,10 +17,6 @@ const eventsHaveDifferentDates = events.some(
|
|||
);
|
||||
const date = eventsHaveDifferentDates ? { year } : events[0]!.date;
|
||||
const multiple = events.flatMap(({ events }) => events).length > 1;
|
||||
|
||||
if (year === 856) {
|
||||
console.log({ eventsHaveDifferentDates, events: events.map(({ date }) => date) });
|
||||
}
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
|
|
@ -6,10 +6,17 @@ import AppLayout from "components/AppLayout/AppLayout.astro";
|
|||
import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro";
|
||||
import Card from "components/Card.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
import type { WordingKey } from "src/i18n/wordings-keys";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
|
||||
const response = await fetchOr404(() => payload.getChronologyEvents());
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const events = response.data;
|
||||
|
||||
const events = await payload.getChronologyEvents();
|
||||
const groupedEvents = groupBy(events, (event) => event.date.year);
|
||||
const { getLocalizedUrl, t, formatTimelineDate } = await getI18n(Astro.locals.currentLocale);
|
||||
---
|
||||
|
|
|
@ -13,14 +13,12 @@ import { fetchOr404 } from "src/utils/responses";
|
|||
import { getFileIcon } from "src/utils/attributes";
|
||||
|
||||
const id = Astro.params.id!;
|
||||
const video = await fetchOr404(() => payload.getVideoByID(id));
|
||||
if (video instanceof Response) {
|
||||
return video;
|
||||
const response = await fetchOr404(() => payload.getVideoByID(id));
|
||||
if (response instanceof Response) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const { getLocalizedMatch, t, formatFilesize, formatDate } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
Astro.locals.sdkCalls.add(response.endpointCalled);
|
||||
const video = response.data;
|
||||
const {
|
||||
translations,
|
||||
attributes,
|
||||
|
@ -34,6 +32,10 @@ const {
|
|||
mimeType,
|
||||
} = video;
|
||||
|
||||
const { getLocalizedMatch, t, formatFilesize, formatDate } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
|
||||
const { pretitle, title, subtitle, description, language } = getLocalizedMatch(translations);
|
||||
|
||||
const metaAttributes = [
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
|
||||
const getUnlocalizedPathname = (pathname: string): string => {
|
||||
for (const locale of contextCache.locales) {
|
||||
|
|
|
@ -1,177 +1,177 @@
|
|||
{
|
||||
"disclaimer": "Usage subject to terms: https://openexchangerates.org/terms",
|
||||
"license": "https://openexchangerates.org/license",
|
||||
"timestamp": 1719518400,
|
||||
"timestamp": 1719604800,
|
||||
"base": "USD",
|
||||
"rates": {
|
||||
"AED": 3.673,
|
||||
"AFN": 70,
|
||||
"ALL": 93.656243,
|
||||
"AMD": 388.12,
|
||||
"ANG": 1.803057,
|
||||
"AFN": 71.063715,
|
||||
"ALL": 93.708843,
|
||||
"AMD": 388.16,
|
||||
"ANG": 1.802366,
|
||||
"AOA": 853.629,
|
||||
"ARS": 911.0001,
|
||||
"AUD": 1.504506,
|
||||
"ARS": 911.483,
|
||||
"AUD": 1.49888,
|
||||
"AWG": 1.8025,
|
||||
"AZN": 1.7,
|
||||
"BAM": 1.827765,
|
||||
"BAM": 1.828243,
|
||||
"BBD": 2,
|
||||
"BDT": 117.53659,
|
||||
"BGN": 1.826865,
|
||||
"BHD": 0.376928,
|
||||
"BIF": 2882.5,
|
||||
"BDT": 117.567308,
|
||||
"BGN": 1.824066,
|
||||
"BHD": 0.376918,
|
||||
"BIF": 2877.015756,
|
||||
"BMD": 1,
|
||||
"BND": 1.357361,
|
||||
"BOB": 6.912666,
|
||||
"BRL": 5.5062,
|
||||
"BND": 1.355347,
|
||||
"BOB": 6.913636,
|
||||
"BRL": 5.5903,
|
||||
"BSD": 1,
|
||||
"BTC": 0.000016286712,
|
||||
"BTN": 83.510338,
|
||||
"BWP": 13.649322,
|
||||
"BYN": 3.274029,
|
||||
"BZD": 2.016506,
|
||||
"CAD": 1.36937,
|
||||
"CDF": 2860,
|
||||
"CHF": 0.898769,
|
||||
"CLF": 0.034588,
|
||||
"CLP": 954.24,
|
||||
"CNH": 7.303225,
|
||||
"CNY": 7.2683,
|
||||
"COP": 4147.064273,
|
||||
"CRC": 523.036765,
|
||||
"BTC": 0.00001666854,
|
||||
"BTN": 83.51892,
|
||||
"BWP": 13.588017,
|
||||
"BYN": 3.274442,
|
||||
"BZD": 2.016789,
|
||||
"CAD": 1.368179,
|
||||
"CDF": 2843.605253,
|
||||
"CHF": 0.898334,
|
||||
"CLF": 0.034326,
|
||||
"CLP": 947.15,
|
||||
"CNH": 7.3002,
|
||||
"CNY": 7.2677,
|
||||
"COP": 4182.257769,
|
||||
"CRC": 523.103268,
|
||||
"CUC": 1,
|
||||
"CUP": 25.75,
|
||||
"CVE": 103.063904,
|
||||
"CZK": 23.432,
|
||||
"DJF": 177.5,
|
||||
"DKK": 6.969109,
|
||||
"DOP": 59.2,
|
||||
"DZD": 134.481574,
|
||||
"EGP": 48.0279,
|
||||
"CVE": 103.074514,
|
||||
"CZK": 23.392,
|
||||
"DJF": 177.827972,
|
||||
"DKK": 6.962786,
|
||||
"DOP": 59.096817,
|
||||
"DZD": 134.738656,
|
||||
"EGP": 48.0286,
|
||||
"ERN": 15,
|
||||
"ETB": 57.750301,
|
||||
"EUR": 0.934324,
|
||||
"FJD": 2.24125,
|
||||
"FKP": 0.791234,
|
||||
"GBP": 0.791234,
|
||||
"ETB": 57.758394,
|
||||
"EUR": 0.933607,
|
||||
"FJD": 2.2387,
|
||||
"FKP": 0.791061,
|
||||
"GBP": 0.791061,
|
||||
"GEL": 2.8,
|
||||
"GGP": 0.791234,
|
||||
"GHS": 15.25,
|
||||
"GIP": 0.791234,
|
||||
"GGP": 0.791061,
|
||||
"GHS": 15.25842,
|
||||
"GIP": 0.791061,
|
||||
"GMD": 67.775,
|
||||
"GNF": 8595,
|
||||
"GTQ": 7.773841,
|
||||
"GYD": 209.310316,
|
||||
"HKD": 7.808725,
|
||||
"HNL": 24.762821,
|
||||
"HRK": 7.039709,
|
||||
"HTG": 132.614267,
|
||||
"HUF": 370.35232,
|
||||
"IDR": 16384.008966,
|
||||
"ILS": 3.75783,
|
||||
"IMP": 0.791234,
|
||||
"INR": 83.466142,
|
||||
"IQD": 1310.629281,
|
||||
"GNF": 8612.182198,
|
||||
"GTQ": 7.771251,
|
||||
"GYD": 209.334678,
|
||||
"HKD": 7.809383,
|
||||
"HNL": 24.765136,
|
||||
"HRK": 7.034895,
|
||||
"HTG": 132.555762,
|
||||
"HUF": 368.794829,
|
||||
"IDR": 16351.422732,
|
||||
"ILS": 3.76585,
|
||||
"IMP": 0.791061,
|
||||
"INR": 83.388488,
|
||||
"IQD": 1310.765417,
|
||||
"IRR": 42100,
|
||||
"ISK": 139.14,
|
||||
"JEP": 0.791234,
|
||||
"JMD": 156.065666,
|
||||
"ISK": 138.83,
|
||||
"JEP": 0.791061,
|
||||
"JMD": 156.080264,
|
||||
"JOD": 0.7087,
|
||||
"JPY": 160.819,
|
||||
"KES": 129,
|
||||
"KGS": 86.45,
|
||||
"KHR": 4116,
|
||||
"KMF": 460.04988,
|
||||
"JPY": 160.8655,
|
||||
"KES": 129.25,
|
||||
"KGS": 86.4454,
|
||||
"KHR": 4110.671159,
|
||||
"KMF": 459.849919,
|
||||
"KPW": 900,
|
||||
"KRW": 1387.206613,
|
||||
"KWD": 0.306775,
|
||||
"KYD": 0.833746,
|
||||
"KZT": 466.751615,
|
||||
"LAK": 22077.44273,
|
||||
"LBP": 89576.348385,
|
||||
"LKR": 306.055904,
|
||||
"LRD": 194.492735,
|
||||
"LSL": 18.359053,
|
||||
"LYD": 4.87349,
|
||||
"MAD": 9.939151,
|
||||
"MDL": 17.849263,
|
||||
"MGA": 4476.966706,
|
||||
"MKD": 57.476359,
|
||||
"KRW": 1379.946543,
|
||||
"KWD": 0.306773,
|
||||
"KYD": 0.833423,
|
||||
"KZT": 466.81538,
|
||||
"LAK": 22075,
|
||||
"LBP": 89600,
|
||||
"LKR": 306.087327,
|
||||
"LRD": 194.450023,
|
||||
"LSL": 18.18,
|
||||
"LYD": 4.875316,
|
||||
"MAD": 9.940081,
|
||||
"MDL": 17.830168,
|
||||
"MGA": 4477.581302,
|
||||
"MKD": 57.45758,
|
||||
"MMK": 2481.91,
|
||||
"MNT": 3450,
|
||||
"MOP": 8.047221,
|
||||
"MRU": 39.452362,
|
||||
"MUR": 46.899999,
|
||||
"MOP": 8.044281,
|
||||
"MRU": 39.462689,
|
||||
"MUR": 47.2,
|
||||
"MVR": 15.405,
|
||||
"MWK": 1734.657401,
|
||||
"MXN": 18.41087,
|
||||
"MYR": 4.7195,
|
||||
"MZN": 63.850001,
|
||||
"NAD": 18.359053,
|
||||
"MWK": 1734.567667,
|
||||
"MXN": 18.292389,
|
||||
"MYR": 4.7175,
|
||||
"MZN": 63.899991,
|
||||
"NAD": 18.36094,
|
||||
"NGN": 1515.9,
|
||||
"NIO": 36.825702,
|
||||
"NOK": 10.666237,
|
||||
"NPR": 133.611854,
|
||||
"NZD": 1.643791,
|
||||
"OMR": 0.384955,
|
||||
"NIO": 36.81119,
|
||||
"NOK": 10.675842,
|
||||
"NPR": 133.327095,
|
||||
"NZD": 1.641672,
|
||||
"OMR": 0.384947,
|
||||
"PAB": 1,
|
||||
"PEN": 3.823237,
|
||||
"PGK": 3.904308,
|
||||
"PHP": 58.639498,
|
||||
"PKR": 278.503056,
|
||||
"PLN": 4.029254,
|
||||
"PYG": 7540.098869,
|
||||
"QAR": 3.649042,
|
||||
"RON": 4.6507,
|
||||
"RSD": 109.379523,
|
||||
"RUB": 84.998456,
|
||||
"RWF": 1306.142519,
|
||||
"SAR": 3.751634,
|
||||
"PEN": 3.83492,
|
||||
"PGK": 3.851055,
|
||||
"PHP": 58.433004,
|
||||
"PKR": 278.529263,
|
||||
"PLN": 4.025131,
|
||||
"PYG": 7540.873261,
|
||||
"QAR": 3.647595,
|
||||
"RON": 4.6472,
|
||||
"RSD": 109.245,
|
||||
"RUB": 85.744825,
|
||||
"RWF": 1306.491928,
|
||||
"SAR": 3.751727,
|
||||
"SBD": 8.43942,
|
||||
"SCR": 13.796937,
|
||||
"SCR": 13.867107,
|
||||
"SDG": 601,
|
||||
"SEK": 10.62955,
|
||||
"SGD": 1.358205,
|
||||
"SHP": 0.791234,
|
||||
"SEK": 10.596578,
|
||||
"SGD": 1.35596,
|
||||
"SHP": 0.791061,
|
||||
"SLL": 20969.5,
|
||||
"SOS": 571.751991,
|
||||
"SRD": 30.623,
|
||||
"SOS": 571.49784,
|
||||
"SRD": 30.8385,
|
||||
"SSP": 130.26,
|
||||
"STD": 22281.8,
|
||||
"STN": 22.899229,
|
||||
"SVC": 8.754335,
|
||||
"STN": 22.878594,
|
||||
"SVC": 8.755235,
|
||||
"SYP": 2512.53,
|
||||
"SZL": 18.178413,
|
||||
"THB": 36.7685,
|
||||
"TJS": 10.65474,
|
||||
"SZL": 18.183346,
|
||||
"THB": 36.719,
|
||||
"TJS": 10.656085,
|
||||
"TMT": 3.51,
|
||||
"TND": 3.136769,
|
||||
"TOP": 2.36092,
|
||||
"TRY": 32.836478,
|
||||
"TTD": 6.7978,
|
||||
"TWD": 32.560001,
|
||||
"TZS": 2626.295328,
|
||||
"UAH": 40.51595,
|
||||
"UGX": 3711.539326,
|
||||
"TND": 3.1465,
|
||||
"TOP": 2.363716,
|
||||
"TRY": 32.656998,
|
||||
"TTD": 6.798721,
|
||||
"TWD": 32.5085,
|
||||
"TZS": 2635,
|
||||
"UAH": 40.51974,
|
||||
"UGX": 3712.013854,
|
||||
"USD": 1,
|
||||
"UYU": 39.578963,
|
||||
"UZS": 12585.732694,
|
||||
"VES": 36.35908,
|
||||
"VND": 25455.008685,
|
||||
"UYU": 39.446434,
|
||||
"UZS": 12586.719022,
|
||||
"VES": 36.390223,
|
||||
"VND": 25455.011984,
|
||||
"VUV": 118.722,
|
||||
"WST": 2.8,
|
||||
"XAF": 612.876514,
|
||||
"XAG": 0.0345453,
|
||||
"XAU": 0.00042988,
|
||||
"XAF": 612.406173,
|
||||
"XAG": 0.03435128,
|
||||
"XAU": 0.00043009,
|
||||
"XCD": 2.70255,
|
||||
"XDR": 0.759718,
|
||||
"XOF": 612.876514,
|
||||
"XPD": 0.00108638,
|
||||
"XPF": 111.494537,
|
||||
"XPT": 0.00101314,
|
||||
"XDR": 0.759476,
|
||||
"XOF": 612.406173,
|
||||
"XPD": 0.00104121,
|
||||
"XPF": 111.408973,
|
||||
"XPT": 0.00100687,
|
||||
"YER": 250.399984,
|
||||
"ZAR": 18.4643,
|
||||
"ZMW": 25.735569,
|
||||
"ZAR": 18.190651,
|
||||
"ZMW": 25.739177,
|
||||
"ZWL": 322
|
||||
}
|
||||
}
|
|
@ -2082,30 +2082,17 @@ export type EndpointAllSDKUrls = {
|
|||
|
||||
// SDK
|
||||
|
||||
type GetPayloadSDKParams = {
|
||||
apiURL: string;
|
||||
email: string;
|
||||
password: string;
|
||||
tokenCache?: {
|
||||
set: (token: string, expirationTimestamp: number) => void;
|
||||
get: () => string | undefined;
|
||||
};
|
||||
responseCache?: {
|
||||
set: (url: string, response: any) => void;
|
||||
get: (url: string) => any | undefined;
|
||||
};
|
||||
};
|
||||
|
||||
const logResponse = (res: Response) => console.log(res.status, res.statusText, res.url);
|
||||
|
||||
export const getSDKEndpoint = {
|
||||
getConfigEndpoint: () => `/globals/${Collections.WebsiteConfig}/config`,
|
||||
getFolderEndpoint: (slug: string) => `/${Collections.Folders}/slug/${slug}`,
|
||||
getFolderSlugsEndpoint: () => `/${Collections.Folders}/slugs`,
|
||||
getLanguagesEndpoint: () => `/${Collections.Languages}/all`,
|
||||
getCurrenciesEndpoint: () => `/${Collections.Currencies}/all`,
|
||||
getWordingsEndpoint: () => `/${Collections.Wordings}/all`,
|
||||
getPageEndpoint: (slug: string) => `/${Collections.Pages}/slug/${slug}`,
|
||||
getPageSlugsEndpoint: () => `/${Collections.Pages}/slugs`,
|
||||
getCollectibleEndpoint: (slug: string) => `/${Collections.Collectibles}/slug/${slug}`,
|
||||
getCollectibleSlugsEndpoint: () => `/${Collections.Collectibles}/slugs`,
|
||||
getCollectibleScansEndpoint: (slug: string) => `/${Collections.Collectibles}/slug/${slug}/scans`,
|
||||
getCollectibleScanPageEndpoint: (slug: string, index: string) =>
|
||||
`/${Collections.Collectibles}/slug/${slug}/scans/${index}`,
|
||||
|
@ -2124,21 +2111,51 @@ export const getSDKEndpoint = {
|
|||
getLoginEndpoint: () => `/${Collections.Recorders}/login`,
|
||||
};
|
||||
|
||||
export const getPayloadSDK = ({
|
||||
apiURL,
|
||||
email,
|
||||
password,
|
||||
tokenCache,
|
||||
responseCache,
|
||||
}: GetPayloadSDKParams) => {
|
||||
const refreshToken = async () => {
|
||||
const loginUrl = `${apiURL}${getSDKEndpoint.getLoginEndpoint()}`;
|
||||
type PayloadSDKResponse<T> = {
|
||||
data: T;
|
||||
endpointCalled: string;
|
||||
};
|
||||
|
||||
type PayloadTokenCache = {
|
||||
set: (token: string, expirationTimestamp: number) => void;
|
||||
get: () => string | undefined;
|
||||
};
|
||||
|
||||
type PayloadDataCache = {
|
||||
set: (url: string, response: any) => void;
|
||||
get: (url: string) => any | undefined;
|
||||
};
|
||||
|
||||
export class PayloadSDK {
|
||||
private tokenCache: PayloadTokenCache | undefined;
|
||||
private dataCache: PayloadDataCache | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly apiURL: string,
|
||||
private readonly email: string,
|
||||
private readonly password: string
|
||||
) {}
|
||||
|
||||
addTokenCache(tokenCache: PayloadTokenCache) {
|
||||
this.tokenCache = tokenCache;
|
||||
}
|
||||
|
||||
addDataCache(dataCache: PayloadDataCache) {
|
||||
this.dataCache = dataCache;
|
||||
}
|
||||
|
||||
private logResponse(res: Response) {
|
||||
console.log(res.status, res.statusText, res.url);
|
||||
}
|
||||
|
||||
private async refreshToken() {
|
||||
const loginUrl = `${this.apiURL}${getSDKEndpoint.getLoginEndpoint()}`;
|
||||
const loginResult = await fetch(loginUrl, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email, password }),
|
||||
body: JSON.stringify({ email: this.email, password: this.password }),
|
||||
});
|
||||
logResponse(loginResult);
|
||||
this.logResponse(loginResult);
|
||||
|
||||
if (loginResult.status !== 200) {
|
||||
throw new Error("Unable to login");
|
||||
|
@ -2148,77 +2165,104 @@ export const getPayloadSDK = ({
|
|||
token: string;
|
||||
exp: number;
|
||||
};
|
||||
tokenCache?.set(token, exp);
|
||||
this.tokenCache?.set(token, exp);
|
||||
return token;
|
||||
};
|
||||
}
|
||||
|
||||
const request = async (endpoint: string): Promise<any> => {
|
||||
const cachedResponse = responseCache?.get(endpoint);
|
||||
async request<T>(endpoint: string): Promise<PayloadSDKResponse<T>> {
|
||||
const cachedResponse = this.dataCache?.get(endpoint);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
const result = await fetch(`${apiURL}${endpoint}`, {
|
||||
const result = await fetch(`${this.apiURL}${endpoint}`, {
|
||||
headers: {
|
||||
Authorization: `JWT ${tokenCache?.get() ?? (await refreshToken())}`,
|
||||
Authorization: `JWT ${this.tokenCache?.get() ?? (await this.refreshToken())}`,
|
||||
},
|
||||
});
|
||||
logResponse(result);
|
||||
this.logResponse(result);
|
||||
|
||||
if (!result.ok) {
|
||||
throw new Error("Unhandled fetch error");
|
||||
}
|
||||
|
||||
const data = await result.json();
|
||||
responseCache?.set(endpoint, data);
|
||||
return data;
|
||||
};
|
||||
const response = { data: await result.json(), endpointCalled: endpoint };
|
||||
this.dataCache?.set(endpoint, response);
|
||||
return response;
|
||||
}
|
||||
|
||||
return {
|
||||
getConfig: async (): Promise<EndpointWebsiteConfig> =>
|
||||
await request(getSDKEndpoint.getConfigEndpoint()),
|
||||
getFolder: async (slug: string): Promise<EndpointFolder> =>
|
||||
await request(getSDKEndpoint.getFolderEndpoint(slug)),
|
||||
getLanguages: async (): Promise<Language[]> =>
|
||||
await request(getSDKEndpoint.getLanguagesEndpoint()),
|
||||
getCurrencies: async (): Promise<Currency[]> =>
|
||||
await request(getSDKEndpoint.getCurrenciesEndpoint()),
|
||||
getWordings: async (): Promise<EndpointWording[]> =>
|
||||
await request(getSDKEndpoint.getWordingsEndpoint()),
|
||||
getPage: async (slug: string): Promise<EndpointPage> =>
|
||||
await request(getSDKEndpoint.getPageEndpoint(slug)),
|
||||
getCollectible: async (slug: string): Promise<EndpointCollectible> =>
|
||||
await request(getSDKEndpoint.getCollectibleEndpoint(slug)),
|
||||
getCollectibleScans: async (slug: string): Promise<EndpointCollectibleScans> =>
|
||||
await request(getSDKEndpoint.getCollectibleScansEndpoint(slug)),
|
||||
getCollectibleScanPage: async (
|
||||
slug: string,
|
||||
index: string
|
||||
): Promise<EndpointCollectibleScanPage> =>
|
||||
await request(getSDKEndpoint.getCollectibleScanPageEndpoint(slug, index)),
|
||||
getCollectibleGallery: async (slug: string): Promise<EndpointCollectibleGallery> =>
|
||||
await request(getSDKEndpoint.getCollectibleGalleryEndpoint(slug)),
|
||||
getCollectibleGalleryImage: async (
|
||||
slug: string,
|
||||
index: string
|
||||
): Promise<EndpointCollectibleGalleryImage> =>
|
||||
await request(getSDKEndpoint.getCollectibleGalleryImageEndpoint(slug, index)),
|
||||
getChronologyEvents: async (): Promise<EndpointChronologyEvent[]> =>
|
||||
await request(getSDKEndpoint.getChronologyEventsEndpoint()),
|
||||
getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> =>
|
||||
await request(getSDKEndpoint.getChronologyEventByIDEndpoint(id)),
|
||||
getImageByID: async (id: string): Promise<EndpointImage> =>
|
||||
await request(getSDKEndpoint.getImageByIDEndpoint(id)),
|
||||
getAudioByID: async (id: string): Promise<EndpointAudio> =>
|
||||
await request(getSDKEndpoint.getAudioByIDEndpoint(id)),
|
||||
getVideoByID: async (id: string): Promise<EndpointVideo> =>
|
||||
await request(getSDKEndpoint.getVideoByIDEndpoint(id)),
|
||||
getFileByID: async (id: string): Promise<EndpointFile> =>
|
||||
await request(getSDKEndpoint.getFileByIDEndpoint(id)),
|
||||
getRecorderByID: async (id: string): Promise<EndpointRecorder> =>
|
||||
await request(getSDKEndpoint.getRecorderByIDEndpoint(id)),
|
||||
getAllSdkUrls: async (): Promise<EndpointAllSDKUrls> =>
|
||||
await request(getSDKEndpoint.getAllSDKUrlsEndpoint()),
|
||||
request: async (pathname: string): Promise<any> => await request(pathname),
|
||||
};
|
||||
};
|
||||
async getConfig(): Promise<PayloadSDKResponse<EndpointWebsiteConfig>> {
|
||||
return await this.request(getSDKEndpoint.getConfigEndpoint());
|
||||
}
|
||||
async getFolder(slug: string): Promise<PayloadSDKResponse<EndpointFolder>> {
|
||||
return await this.request(getSDKEndpoint.getFolderEndpoint(slug));
|
||||
}
|
||||
async getFolderSlugs(): Promise<PayloadSDKResponse<string[]>> {
|
||||
return await this.request(getSDKEndpoint.getFolderSlugsEndpoint());
|
||||
}
|
||||
async getLanguages(): Promise<PayloadSDKResponse<Language[]>> {
|
||||
return await this.request(getSDKEndpoint.getLanguagesEndpoint());
|
||||
}
|
||||
async getCurrencies(): Promise<PayloadSDKResponse<Currency[]>> {
|
||||
return await this.request(getSDKEndpoint.getCurrenciesEndpoint());
|
||||
}
|
||||
async getWordings(): Promise<PayloadSDKResponse<EndpointWording[]>> {
|
||||
return await this.request(getSDKEndpoint.getWordingsEndpoint());
|
||||
}
|
||||
async getPage(slug: string): Promise<PayloadSDKResponse<EndpointPage>> {
|
||||
return await this.request(getSDKEndpoint.getPageEndpoint(slug));
|
||||
}
|
||||
async getPageSlugs(): Promise<PayloadSDKResponse<string[]>> {
|
||||
return await this.request(getSDKEndpoint.getPageSlugsEndpoint());
|
||||
}
|
||||
async getCollectible(slug: string): Promise<PayloadSDKResponse<EndpointCollectible>> {
|
||||
return await this.request(getSDKEndpoint.getCollectibleEndpoint(slug));
|
||||
}
|
||||
async getCollectibleSlugs(): Promise<PayloadSDKResponse<string[]>> {
|
||||
return await this.request(getSDKEndpoint.getCollectibleSlugsEndpoint());
|
||||
}
|
||||
async getCollectibleScans(slug: string): Promise<PayloadSDKResponse<EndpointCollectibleScans>> {
|
||||
return await this.request(getSDKEndpoint.getCollectibleScansEndpoint(slug));
|
||||
}
|
||||
async getCollectibleScanPage(
|
||||
slug: string,
|
||||
index: string
|
||||
): Promise<PayloadSDKResponse<EndpointCollectibleScanPage>> {
|
||||
return await this.request(getSDKEndpoint.getCollectibleScanPageEndpoint(slug, index));
|
||||
}
|
||||
async getCollectibleGallery(
|
||||
slug: string
|
||||
): Promise<PayloadSDKResponse<EndpointCollectibleGallery>> {
|
||||
return await this.request(getSDKEndpoint.getCollectibleGalleryEndpoint(slug));
|
||||
}
|
||||
async getCollectibleGalleryImage(
|
||||
slug: string,
|
||||
index: string
|
||||
): Promise<PayloadSDKResponse<EndpointCollectibleGalleryImage>> {
|
||||
return await this.request(getSDKEndpoint.getCollectibleGalleryImageEndpoint(slug, index));
|
||||
}
|
||||
async getChronologyEvents(): Promise<PayloadSDKResponse<EndpointChronologyEvent[]>> {
|
||||
return await this.request(getSDKEndpoint.getChronologyEventsEndpoint());
|
||||
}
|
||||
async getChronologyEventByID(id: string): Promise<PayloadSDKResponse<EndpointChronologyEvent>> {
|
||||
return await this.request(getSDKEndpoint.getChronologyEventByIDEndpoint(id));
|
||||
}
|
||||
async getImageByID(id: string): Promise<PayloadSDKResponse<EndpointImage>> {
|
||||
return await this.request(getSDKEndpoint.getImageByIDEndpoint(id));
|
||||
}
|
||||
async getAudioByID(id: string): Promise<PayloadSDKResponse<EndpointAudio>> {
|
||||
return await this.request(getSDKEndpoint.getAudioByIDEndpoint(id));
|
||||
}
|
||||
async getVideoByID(id: string): Promise<PayloadSDKResponse<EndpointVideo>> {
|
||||
return await this.request(getSDKEndpoint.getVideoByIDEndpoint(id));
|
||||
}
|
||||
async getFileByID(id: string): Promise<PayloadSDKResponse<EndpointFile>> {
|
||||
return await this.request(getSDKEndpoint.getFileByIDEndpoint(id));
|
||||
}
|
||||
async getRecorderByID(id: string): Promise<PayloadSDKResponse<EndpointRecorder>> {
|
||||
return await this.request(getSDKEndpoint.getRecorderByIDEndpoint(id));
|
||||
}
|
||||
async getAllSdkUrls(): Promise<PayloadSDKResponse<EndpointAllSDKUrls>> {
|
||||
return await this.request(getSDKEndpoint.getAllSDKUrlsEndpoint());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
type RichTextContent,
|
||||
type RichTextNode,
|
||||
} from "src/shared/payload/payload-sdk";
|
||||
import { contextCache } from "src/cache/contextCache";
|
||||
import { contextCache } from "src/utils/payload";
|
||||
|
||||
export const formatLocale = (code: string): string =>
|
||||
contextCache.locales.find(({ id }) => id === code)?.name ?? code;
|
||||
|
|
|
@ -1,28 +1,20 @@
|
|||
import { dataCache } from "src/cache/dataCache";
|
||||
import { getPayloadSDK } from "src/shared/payload/payload-sdk";
|
||||
import { ContextCache } from "src/cache/contextCache";
|
||||
import { DataCache } from "src/cache/dataCache";
|
||||
import { PageCache } from "src/cache/pageCache";
|
||||
import { TokenCache } from "src/cache/tokenCache";
|
||||
import { PayloadSDK } from "src/shared/payload/payload-sdk";
|
||||
|
||||
let token: string | undefined = undefined;
|
||||
let expiration: number | undefined = undefined;
|
||||
const payload = new PayloadSDK(
|
||||
import.meta.env.PAYLOAD_API_URL,
|
||||
import.meta.env.PAYLOAD_USER,
|
||||
import.meta.env.PAYLOAD_PASSWORD
|
||||
);
|
||||
|
||||
export const payload = getPayloadSDK({
|
||||
apiURL: import.meta.env.PAYLOAD_API_URL,
|
||||
email: import.meta.env.PAYLOAD_USER,
|
||||
password: import.meta.env.PAYLOAD_PASSWORD,
|
||||
tokenCache: {
|
||||
get: () => {
|
||||
if (!token) return undefined;
|
||||
if (!expiration || expiration < Date.now()) {
|
||||
console.log("[PayloadSDK] No token to be retrieved or the token expired");
|
||||
return undefined;
|
||||
}
|
||||
return token;
|
||||
},
|
||||
set: (newToken, newExpiration) => {
|
||||
token = newToken;
|
||||
expiration = newExpiration * 1000;
|
||||
const diffInMinutes = Math.floor((expiration - Date.now()) / 1000 / 60);
|
||||
console.log("[PayloadSDK] New token set. TTL is", diffInMinutes, "minutes.");
|
||||
},
|
||||
},
|
||||
responseCache: dataCache,
|
||||
});
|
||||
const contextCache = new ContextCache(payload);
|
||||
const pageCache = new PageCache(payload);
|
||||
const dataCache = new DataCache(payload, (urls) => pageCache.invalidate(urls));
|
||||
|
||||
payload.addTokenCache(new TokenCache());
|
||||
payload.addDataCache(dataCache);
|
||||
|
||||
export { payload, contextCache, pageCache, dataCache };
|
||||
|
|
Loading…
Reference in New Issue