2024-03-04 10:51:47 +01:00

384 lines
9.5 KiB
TypeScript

import {
CollectibleBindingTypes,
CollectibleNature,
CollectiblePageOrders,
Collections,
PageType,
RichTextContent,
} from "./constants";
import { Collectible, Currency, GenericContent, Language, Page } from "./types/collections";
class NodeCache {
constructor(_params: any) {}
getTtl(_key: string): number | undefined {
return undefined;
}
get<T>(_key: string): T | undefined {
return undefined;
}
set<T>(_key: string, _value: T, _ttl: number | string) {}
}
// END MOCKING SECTION
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<string> => {
const cachedToken = CACHE.get<string>(TOKEN_KEY);
if (cachedToken !== undefined) {
const cachedTokenTtl = CACHE.getTtl(TOKEN_KEY) as number;
const diffInMinutes = Math.floor((cachedTokenTtl - Date.now()) / 1000 / 60);
console.log("Retrieved token from cache. TTL is", diffInMinutes, "minutes.");
return cachedToken;
}
console.log("Refreshing token");
return await refreshToken();
};
const injectAuth = async (init?: RequestInit): Promise<RequestInit> => ({
...init,
headers: { ...init?.headers, Authorization: `JWT ${await getToken()}` },
});
const logResponse = (res: Response) => console.log(res.status, res.statusText, res.url);
const payloadApiUrl = (collection: Collections, endpoint?: string): string =>
`${import.meta.env.PAYLOAD_API_URL}/${collection}${endpoint === undefined ? "" : `/${endpoint}`}`;
const request = async (url: string, init?: RequestInit): Promise<Response> => {
const result = await fetch(url, await injectAuth(init));
logResponse(result);
if (result.status !== 200) {
throw new Error("Unhandled fetch error");
}
return result;
};
// SDK and Types
export type EndpointWeapon = EndpointBasicWeapon & {
appearances: {
categories: string[];
translations: {
language: string;
sourceLanguage: string;
name: string;
description?: string;
level1?: string;
level2?: string;
level3?: string;
level4?: string;
transcribers: string[];
translators: string[];
proofreaders: string[];
}[];
}[];
group?: {
slug: string;
translations: { language: string; name: string }[];
weapons: EndpointBasicWeapon[];
};
};
export type EndpointBasicWeapon = {
slug: string;
type: string;
categories: string[];
translations: { language: string; name: string; aliases: string[] }[];
images?: {
previewCard: PayloadImage;
thumbnailHeader: PayloadImage;
lightBox: PayloadImage;
openGraph: PayloadImage;
};
};
export type EndpointEra = {
slug: string;
startingYear: number;
endingYear: number;
translations: {
language: string;
title: string;
description?: string;
}[];
items: {
date: {
year: number;
month?: number;
day?: number;
};
events: {
translations: {
language: string;
sourceLanguage: string;
title?: string;
description?: string;
notes?: string;
transcribers: string[];
translators: string[];
proofreaders: string[];
}[];
}[];
createdAt: Date;
updatedAt: Date;
updatedBy: string;
}[];
};
export type EndpointFolder = EndpointFolderPreview & {
sections:
| { type: "single"; subfolders: EndpointFolderPreview[] }
| {
type: "multiple";
sections: {
translations: { language: string; name: string }[];
subfolders: EndpointFolderPreview[];
}[];
};
files: (
| {
relationTo: "collectibles";
value: Collectible;
}
| {
relationTo: "pages";
value: Page;
}
)[];
};
export type EndpointFolderPreview = {
slug: string;
icon?: string;
translations: {
language: string;
name: string;
description?: RichTextContent;
}[];
lightThumbnail?: PayloadImage;
darkThumbnail?: PayloadImage;
};
export type EndpointRecorder = {
id: string;
username: string;
avatar?: PayloadImage;
languages: string[];
biographies: {
language: string;
biography: RichTextContent;
}[];
};
export type EndpointWording = {
name: string;
translations: {
language: string;
name: string;
}[];
};
export type EndpointTag = {
slug: string;
translations: {
language: string;
name: string;
}[];
group: string;
};
export type EndpointTagsGroup = {
slug: string;
icon?: string;
translations: {
language: string;
name: string;
}[];
tags: EndpointTag[];
};
export type EndpointPage = {
slug: string;
type: PageType;
thumbnail?: PayloadImage;
authors: string[];
tagGroups: TagGroup[];
translations: {
language: string;
sourceLanguage: string;
pretitle?: string;
title: string;
subtitle?: string;
summary?: RichTextContent;
content: RichTextContent;
transcribers: string[];
translators: string[];
proofreaders: string[];
toc: TableOfContentEntry[];
}[];
status: "draft" | "published";
parentPages: ParentPage[];
};
export type ParentPage = {
slug: string;
collection: Collections;
translations: { language: string; name: string }[];
tag: string;
};
export type EndpointCollectiblePreview = {
slug: string;
thumbnail?: PayloadImage;
translations: {
language: string;
pretitle?: string;
title: string;
subtitle?: string;
description?: RichTextContent;
}[];
tagGroups: TagGroup[];
status: "draft" | "published";
releaseDate?: string;
languages: string[];
};
export type EndpointCollectible = EndpointCollectiblePreview & {
nature: CollectibleNature;
gallery: PayloadImage[];
urls: { url: string; label: string }[];
price?: {
amount: number;
currency: string;
};
size?: {
width: number;
height: number;
thickness?: number;
};
weight?: {
amount: number;
};
pageInfo?: {
pageCount: number;
bindingType?: CollectibleBindingTypes;
pageOrder?: CollectiblePageOrders;
};
subitems: EndpointCollectiblePreview[];
contents: {
content:
| {
relationTo: "pages";
value: Page;
}
| {
relationTo: "generic-contents";
value: GenericContent;
};
range?:
| {
type: "pageRange";
start: number;
end: number;
}
| {
type: "timeRange";
start: string;
end: string;
}
| {
type: "other";
translations: {
language: string;
note: RichTextContent;
}[];
};
}[];
parentPages: ParentPage[];
};
export type TagGroup = { slug: string; icon: string; values: string[] };
export type TableOfContentEntry = {
prefix: string;
title: string;
children: TableOfContentEntry[];
};
export type PayloadImage = {
url: string;
width: number;
height: number;
mimeType: string;
filename: string;
};
export const payload = {
getWeapon: async (slug: string): Promise<EndpointWeapon> =>
await (await request(payloadApiUrl(Collections.Weapons, `slug/${slug}`))).json(),
getEras: async (): Promise<EndpointEra[]> =>
await (await request(payloadApiUrl(Collections.ChronologyEras, `all`))).json(),
getRootFolders: async (): Promise<EndpointFolderPreview[]> =>
await (await request(payloadApiUrl(Collections.Folders, `root`))).json(),
getFolder: async (slug: string): Promise<EndpointFolder> =>
await (await request(payloadApiUrl(Collections.Folders, `slug/${slug}`))).json(),
getLanguages: async (): Promise<Language[]> =>
await (await request(payloadApiUrl(Collections.Languages, `all`))).json(),
getCurrencies: async (): Promise<Currency[]> =>
await (await request(payloadApiUrl(Collections.Currencies, `all`))).json(),
getWordings: async (): Promise<EndpointWording[]> =>
await (await request(payloadApiUrl(Collections.Wordings, `all`))).json(),
getRecorders: async (): Promise<EndpointRecorder[]> =>
await (await request(payloadApiUrl(Collections.Recorders, `all`))).json(),
getTags: async (): Promise<EndpointTag[]> =>
await (await request(payloadApiUrl(Collections.Tags, `all`))).json(),
getTagsGroups: async (): Promise<EndpointTagsGroup[]> =>
await (await request(payloadApiUrl(Collections.TagsGroups, `all`))).json(),
getPage: async (slug: string): Promise<EndpointPage> =>
await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(),
getCollectible: async (slug: string): Promise<EndpointCollectible> =>
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(),
};