diff --git a/src/collections/Audios/Audios.ts b/src/collections/Audios/Audios.ts index b4c204d..0871759 100644 --- a/src/collections/Audios/Audios.ts +++ b/src/collections/Audios/Audios.ts @@ -5,6 +5,7 @@ import { tagsField } from "../../fields/tagsField/tagsField"; import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { buildCollectionConfig } from "../../utils/collectionConfig"; import { createEditor } from "../../utils/editor"; +import { getByID } from "./endpoints/getByID"; const fields = { filename: "filename", @@ -38,6 +39,7 @@ export const Audios = buildCollectionConfig({ mimeTypes: ["audio/*"], disableLocalStorage: true, }, + endpoints: [getByID], fields: [ rowField([ { name: fields.duration, type: "number", min: 0, required: true }, diff --git a/src/collections/Audios/endpoints/getByID.ts b/src/collections/Audios/endpoints/getByID.ts new file mode 100644 index 0000000..7de9ff8 --- /dev/null +++ b/src/collections/Audios/endpoints/getByID.ts @@ -0,0 +1,77 @@ +import payload from "payload"; +import { Collections } from "../../../constants"; +import { EndpointAudio, PayloadMedia } from "../../../sdk"; +import { Audio } from "../../../types/collections"; +import { CollectionEndpoint } from "../../../types/payload"; +import { isNotEmpty, isValidPayloadImage, isValidPayloadMedia } from "../../../utils/asserts"; +import { + convertRTCToEndpointRTC, + convertTagsEndpointTagsGroups, + getLanguageId, +} from "../../../utils/endpoints"; + +export const getByID: CollectionEndpoint = { + method: "get", + path: "/id/:id", + handler: async (req, res) => { + if (!req.user) { + return res.status(403).send({ + errors: [ + { + message: "You are not allowed to perform this action.", + }, + ], + }); + } + + if (!req.params.id) { + return res.status(400).send({ errors: [{ message: "Missing 'id' query params" }] }); + } + + try { + const result = await payload.findByID({ + collection: Collections.Audios, + id: req.params.id, + }); + + if (!isValidPayloadMedia(result)) { + return res.sendStatus(404); + } + + return res.status(200).json(convertAudioToEndpointAudio(result)); + } catch { + return res.sendStatus(404); + } + }, +}; + +export const convertAudioToEndpointAudio = ({ + url, + tags, + translations, + mimeType, + createdAt, + updatedAt, + filename, + filesize, + duration, + id, + thumbnail, +}: Audio & PayloadMedia): EndpointAudio => ({ + url, + tagGroups: convertTagsEndpointTagsGroups(tags), + createdAt, + filename, + filesize, + id, + mimeType, + updatedAt, + translations: + translations?.map(({ language, title, description }) => ({ + language: getLanguageId(language), + title, + ...(isNotEmpty(description) ? { description: convertRTCToEndpointRTC(description) } : {}), + })) ?? [], + duration, + ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), +}); diff --git a/src/collections/ChronologyEvents/endpoints/getAllEndpoint.ts b/src/collections/ChronologyEvents/endpoints/getAllEndpoint.ts index 11ed636..2106ba1 100644 --- a/src/collections/ChronologyEvents/endpoints/getAllEndpoint.ts +++ b/src/collections/ChronologyEvents/endpoints/getAllEndpoint.ts @@ -4,9 +4,10 @@ import { EndpointChronologyEvent, EndpointSource } from "../../../sdk"; import { ChronologyEvent, CollectibleBlock } from "../../../types/collections"; import { CollectionEndpoint } from "../../../types/payload"; import { isDefined, isNotEmpty, isPayloadArrayType, isPayloadType } from "../../../utils/asserts"; -import { getDomainFromUrl, handleRecorder } from "../../../utils/endpoints"; -import { convertCollectibleToPreview } from "../../Collectibles/endpoints/getBySlugEndpoint"; -import { convertPageToPreview } from "../../Pages/endpoints/getBySlugEndpoint"; +import { getDomainFromUrl } from "../../../utils/endpoints"; +import { convertCollectibleToEndpointCollectible } from "../../Collectibles/endpoints/getBySlugEndpoint"; +import { convertPageToEndpointPage } from "../../Pages/endpoints/getBySlugEndpoint"; +import { convertRecorderToEndpointRecorder } from "../../Recorders/endpoints/getByUsername"; export const getAllEndpoint: CollectionEndpoint = { method: "get", @@ -82,9 +83,15 @@ export const eventToEndpointEvent = ({ ...(isNotEmpty(title) ? { title } : {}), ...(isNotEmpty(description) ? { description } : {}), ...(isNotEmpty(notes) ? { notes } : {}), - proofreaders: isPayloadArrayType(proofreaders) ? proofreaders.map(handleRecorder) : [], - transcribers: isPayloadArrayType(transcribers) ? transcribers.map(handleRecorder) : [], - translators: isPayloadArrayType(translators) ? translators.map(handleRecorder) : [], + proofreaders: isPayloadArrayType(proofreaders) + ? proofreaders.map(convertRecorderToEndpointRecorder) + : [], + transcribers: isPayloadArrayType(transcribers) + ? transcribers.map(convertRecorderToEndpointRecorder) + : [], + translators: isPayloadArrayType(translators) + ? translators.map(convertRecorderToEndpointRecorder) + : [], }) ), sources: handleSources(sources), @@ -100,7 +107,7 @@ const handleSources = (sources: ChronologyEvent["events"][number]["sources"]): E if (!isPayloadType(source.collectible)) return []; return { type: "collectible", - collectible: convertCollectibleToPreview(source.collectible), + collectible: convertCollectibleToEndpointCollectible(source.collectible), ...(isDefined(range) ? { range } : {}), }; @@ -108,7 +115,7 @@ const handleSources = (sources: ChronologyEvent["events"][number]["sources"]): E if (!isPayloadType(source.page)) return []; return { type: "page", - page: convertPageToPreview(source.page), + page: convertPageToEndpointPage(source.page), }; case "urlBlock": diff --git a/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts b/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts index 9779aba..594e8f0 100644 --- a/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts +++ b/src/collections/Collectibles/endpoints/getBySlugEndpoint.ts @@ -1,6 +1,6 @@ import { CollectibleNature, Collections } from "../../../constants"; import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; -import { EndpointCollectible, EndpointCollectiblePreview, PayloadImage } from "../../../sdk"; +import { EndpointCollectible, PayloadImage } from "../../../sdk"; import { Collectible } from "../../../types/collections"; import { isDefined, @@ -9,52 +9,76 @@ import { isPayloadType, isPublished, isValidPayloadImage, + isValidPayloadMedia, } from "../../../utils/asserts"; -import { convertTagsToGroups, getDomainFromUrl, handleParentPages } from "../../../utils/endpoints"; -import { convertPageToPreview } from "../../Pages/endpoints/getBySlugEndpoint"; +import { + convertSourceToEndpointSource, + convertTagsEndpointTagsGroups, + getDomainFromUrl, +} from "../../../utils/endpoints"; +import { convertAudioToEndpointAudio } from "../../Audios/endpoints/getByID"; +import { convertPageToEndpointPage } from "../../Pages/endpoints/getBySlugEndpoint"; +import { convertVideoToEndpointVideo } from "../../Videos/endpoints/getByID"; export const getBySlugEndpoint = createGetByEndpoint({ collection: Collections.Collectibles, attribute: "slug", depth: 3, - handler: (collectible: Collectible): EndpointCollectible => { - const { - nature, - urls, - subitems, - gallery, - contents, - priceEnabled, - price, - size, - sizeEnabled, - weight, - weightEnabled, - pageInfo, - pageInfoEnabled, - parentItems, - folders, - backgroundImage, - } = collectible; + handler: (collectible) => convertCollectibleToEndpointCollectible(collectible), +}); - return { - ...convertCollectibleToPreview(collectible), - ...(isValidPayloadImage(backgroundImage) ? { backgroundImage } : {}), - contents: handleContents(contents), - gallery: handleGallery(gallery), - scans: handleScans(collectible.scans), - nature: nature === "Physical" ? CollectibleNature.Physical : CollectibleNature.Digital, - parentPages: handleParentPages({ collectibles: parentItems, folders }), - subitems: isPayloadArrayType(subitems) - ? subitems.filter(isPublished).map(convertCollectibleToPreview) - : [], - urls: urls?.map(({ url }) => ({ url, label: getDomainFromUrl(url) })) ?? [], - ...(weightEnabled && isDefined(weight) ? { weight: weight.amount } : {}), - ...handleSize(size, sizeEnabled), - ...handlePageInfo(pageInfo, pageInfoEnabled), - ...handlePrice(price, priceEnabled), - }; - }, +export const convertCollectibleToEndpointCollectible = ({ + nature, + urls, + subitems, + gallery, + contents, + priceEnabled, + price, + size, + sizeEnabled, + weight, + weightEnabled, + pageInfo, + pageInfoEnabled, + parentItems, + folders, + backgroundImage, + slug, + thumbnail, + translations, + releaseDate, + languages, + scans, + tags, +}: Collectible): EndpointCollectible => ({ + slug, + languages: languages?.map((language) => (isPayloadType(language) ? language.id : language)) ?? [], + ...(isDefined(releaseDate) ? { releaseDate } : {}), + ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), + tagGroups: convertTagsEndpointTagsGroups(tags), + translations: + translations?.map(({ language, title, description, pretitle, subtitle }) => ({ + language: isPayloadType(language) ? language.id : language, + title, + ...(isNotEmpty(pretitle) ? { pretitle } : {}), + ...(isNotEmpty(subtitle) ? { subtitle } : {}), + ...(isNotEmpty(description) ? { description } : {}), + })) ?? [], + ...(isValidPayloadImage(backgroundImage) ? { backgroundImage } : {}), + contents: handleContents(contents), + gallery: handleGallery(gallery), + scans: handleScans(scans), + nature: nature === "Physical" ? CollectibleNature.Physical : CollectibleNature.Digital, + parentPages: convertSourceToEndpointSource({ collectibles: parentItems, folders }), + subitems: isPayloadArrayType(subitems) + ? subitems.filter(isPublished).map(convertCollectibleToEndpointCollectible) + : [], + urls: urls?.map(({ url }) => ({ url, label: getDomainFromUrl(url) })) ?? [], + ...(weightEnabled && isDefined(weight) ? { weight: weight.amount } : {}), + ...handleSize(size, sizeEnabled), + ...handlePageInfo(pageInfo, pageInfoEnabled), + ...handlePrice(price, priceEnabled), }); const handlePrice = ( @@ -173,10 +197,10 @@ const handleContents = (contents: Collectible["contents"]): EndpointCollectible[ const handleContent = (): EndpointCollectible["contents"][number]["content"] | undefined => { switch (content.relationTo) { - case "generic-contents": + case Collections.GenericContents: return isPayloadType(content.value) ? { - relationTo: "generic-contents", + relationTo: Collections.GenericContents, value: { translations: content.value.translations.map(({ language, name }) => ({ language: isPayloadType(language) ? language.id : language, @@ -186,9 +210,19 @@ const handleContents = (contents: Collectible["contents"]): EndpointCollectible[ } : undefined; - case "pages": + case Collections.Pages: return isPayloadType(content.value) && isPublished(content.value) - ? { relationTo: "pages", value: convertPageToPreview(content.value) } + ? { relationTo: Collections.Pages, value: convertPageToEndpointPage(content.value) } + : undefined; + + case Collections.Audios: + return isPayloadType(content.value) && isValidPayloadMedia(content.value) + ? { relationTo: Collections.Audios, value: convertAudioToEndpointAudio(content.value) } + : undefined; + + case Collections.Videos: + return isPayloadType(content.value) && isValidPayloadMedia(content.value) + ? { relationTo: Collections.Videos, value: convertVideoToEndpointVideo(content.value) } : undefined; default: @@ -203,29 +237,3 @@ const handleContents = (contents: Collectible["contents"]): EndpointCollectible[ return [{ content: newContent, range }]; }); }; - -export const convertCollectibleToPreview = ({ - slug, - thumbnail, - translations, - releaseDate, - languages, - tags, -}: Collectible): EndpointCollectiblePreview => { - return { - slug, - languages: - languages?.map((language) => (isPayloadType(language) ? language.id : language)) ?? [], - ...(isDefined(releaseDate) ? { releaseDate } : {}), - ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), - tagGroups: convertTagsToGroups(tags), - translations: - translations?.map(({ language, title, description, pretitle, subtitle }) => ({ - language: isPayloadType(language) ? language.id : language, - title, - ...(isNotEmpty(pretitle) ? { pretitle } : {}), - ...(isNotEmpty(subtitle) ? { subtitle } : {}), - ...(isNotEmpty(description) ? { description } : {}), - })) ?? [], - }; -}; diff --git a/src/collections/Folders/endpoints/getBySlugEndpoint.ts b/src/collections/Folders/endpoints/getBySlugEndpoint.ts index ecb568a..ff91f33 100644 --- a/src/collections/Folders/endpoints/getBySlugEndpoint.ts +++ b/src/collections/Folders/endpoints/getBySlugEndpoint.ts @@ -1,79 +1,95 @@ import { Collections } from "../../../constants"; import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; -import { EndpointFolder, EndpointFolderPreview } from "../../../sdk"; +import { EndpointFolder } from "../../../sdk"; import { Folder, Language } from "../../../types/collections"; -import { isDefined, isNotEmpty, isPayloadType, isPublished } from "../../../utils/asserts"; -import { handleParentPages } from "../../../utils/endpoints"; -import { convertCollectibleToPreview } from "../../Collectibles/endpoints/getBySlugEndpoint"; -import { convertPageToPreview } from "../../Pages/endpoints/getBySlugEndpoint"; +import { + isDefined, + isNotEmpty, + isPayloadType, + isPublished, + isValidPayloadImage, + isValidPayloadMedia, +} from "../../../utils/asserts"; +import { convertSourceToEndpointSource, getLanguageId } from "../../../utils/endpoints"; +import { convertAudioToEndpointAudio } from "../../Audios/endpoints/getByID"; +import { convertCollectibleToEndpointCollectible } from "../../Collectibles/endpoints/getBySlugEndpoint"; +import { convertImageToEndpointImage } from "../../Images/endpoints/getByID"; +import { convertPageToEndpointPage } from "../../Pages/endpoints/getBySlugEndpoint"; +import { convertVideoToEndpointVideo } from "../../Videos/endpoints/getByID"; export const getBySlugEndpoint = createGetByEndpoint({ collection: Collections.Folders, attribute: "slug", depth: 3, - handler: (folder: Folder): EndpointFolder => { - const { sections, files, parentFolders } = folder; - return { - ...convertFolderToPreview(folder), - sections: - sections?.length === 1 - ? { - type: "single", - subfolders: - sections[0]?.subfolders?.filter(isPayloadType).map(convertFolderToPreview) ?? [], - } - : { - type: "multiple", - sections: - sections?.filter(isValidSection).map(({ translations, subfolders }) => ({ - translations: translations.map(({ language, name }) => ({ - language: getLanguageId(language), - name, - })), - subfolders: subfolders.map(convertFolderToPreview), - })) ?? [], - }, - files: - files?.flatMap(({ relationTo, value }) => { - if (!isPayloadType(value) || ("_status" in value && !isPublished(value))) { - return []; - } - - switch (relationTo) { - case "collectibles": - return [{ relationTo, value: convertCollectibleToPreview(value) }]; - case "pages": - return [{ relationTo, value: convertPageToPreview(value) }]; - // TODO: handle media type files - case "images": - return []; - case "audios": - return []; - case "videos": - return []; - } - }) ?? [], - parentPages: handleParentPages({ folders: parentFolders }), - }; - }, + handler: (folder) => convertFolderToEndpointFolder(folder), }); -export const convertFolderToPreview = ({ +export const convertFolderToEndpointFolder = ({ slug, - translations, icon, -}: Folder): EndpointFolderPreview => { - return { - slug, - ...(isDefined(icon) ? { icon } : {}), - translations: - translations?.map(({ language, name, description }) => ({ - language: getLanguageId(language), - name, - ...(isNotEmpty(description) ? { description } : {}), - })) ?? [], - }; -}; + translations, + sections, + files, + parentFolders, +}: Folder): EndpointFolder => ({ + slug, + ...(isDefined(icon) ? { icon } : {}), + translations: + translations?.map(({ language, name, description }) => ({ + language: getLanguageId(language), + name, + ...(isNotEmpty(description) ? { description } : {}), + })) ?? [], + sections: + sections?.length === 1 + ? { + type: "single", + subfolders: + sections[0]?.subfolders?.filter(isPayloadType).map(convertFolderToEndpointFolder) ?? [], + } + : { + type: "multiple", + sections: + sections?.filter(isValidSection).map(({ translations, subfolders }) => ({ + translations: translations.map(({ language, name }) => ({ + language: getLanguageId(language), + name, + })), + subfolders: subfolders.map(convertFolderToEndpointFolder), + })) ?? [], + }, + files: + files?.flatMap(({ relationTo, value }) => { + if (!isPayloadType(value) || ("_status" in value && !isPublished(value))) { + return []; + } + + switch (relationTo) { + case Collections.Collectibles: + return [ + { + relationTo: Collections.Collectibles, + value: convertCollectibleToEndpointCollectible(value), + }, + ]; + case Collections.Pages: + return [{ relationTo: Collections.Pages, value: convertPageToEndpointPage(value) }]; + // TODO: handle media type files + case Collections.Images: + if (!isValidPayloadImage(value)) return []; + return [{ relationTo: Collections.Images, value: convertImageToEndpointImage(value) }]; + case Collections.Audios: + if (!isValidPayloadMedia(value)) return []; + return [{ relationTo: Collections.Audios, value: convertAudioToEndpointAudio(value) }]; + case Collections.Videos: + if (!isValidPayloadMedia(value)) return []; + return [{ relationTo: Collections.Videos, value: convertVideoToEndpointVideo(value) }]; + default: + return []; + } + }) ?? [], + parentPages: convertSourceToEndpointSource({ folders: parentFolders }), +}); const isValidSection = (section: { translations?: @@ -100,6 +116,3 @@ const isValidSection = (section: { } return section.subfolders.every(isPayloadType); }; - -const getLanguageId = (language: string | Language) => - typeof language === "object" ? language.id : language; diff --git a/src/collections/Images/Images.ts b/src/collections/Images/Images.ts index fce321c..d1bce51 100644 --- a/src/collections/Images/Images.ts +++ b/src/collections/Images/Images.ts @@ -3,6 +3,7 @@ import { tagsField } from "../../fields/tagsField/tagsField"; import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { createEditor } from "../../utils/editor"; import { buildImageCollectionConfig } from "../../utils/imageCollectionConfig"; +import { getByID } from "./endpoints/getByID"; const fields = { filename: "filename", @@ -36,6 +37,7 @@ export const Images = buildImageCollectionConfig({ }, ], }, + endpoints: [getByID], fields: [ translatedFields({ name: fields.translations, diff --git a/src/collections/Images/endpoints/getByID.ts b/src/collections/Images/endpoints/getByID.ts new file mode 100644 index 0000000..9047daf --- /dev/null +++ b/src/collections/Images/endpoints/getByID.ts @@ -0,0 +1,77 @@ +import payload from "payload"; +import { Collections } from "../../../constants"; +import { EndpointImage, PayloadImage } from "../../../sdk"; +import { Image } from "../../../types/collections"; +import { CollectionEndpoint } from "../../../types/payload"; +import { isNotEmpty, isValidPayloadImage } from "../../../utils/asserts"; +import { + convertRTCToEndpointRTC, + convertTagsEndpointTagsGroups, + getLanguageId, +} from "../../../utils/endpoints"; + +export const getByID: CollectionEndpoint = { + method: "get", + path: "/id/:id", + handler: async (req, res) => { + if (!req.user) { + return res.status(403).send({ + errors: [ + { + message: "You are not allowed to perform this action.", + }, + ], + }); + } + + if (!req.params.id) { + return res.status(400).send({ errors: [{ message: "Missing 'id' query params" }] }); + } + + try { + const result = await payload.findByID({ + collection: Collections.Images, + id: req.params.id, + }); + + if (!isValidPayloadImage(result)) { + return res.sendStatus(404); + } + + return res.status(200).json(convertImageToEndpointImage(result)); + } catch { + return res.sendStatus(404); + } + }, +}; + +export const convertImageToEndpointImage = ({ + url, + width, + height, + tags, + translations, + mimeType, + createdAt, + updatedAt, + filename, + filesize, + id, +}: Image & PayloadImage): EndpointImage => ({ + url, + width, + height, + tagGroups: convertTagsEndpointTagsGroups(tags), + createdAt, + filename, + filesize, + id, + mimeType, + updatedAt, + translations: + translations?.map(({ language, title, description }) => ({ + language: getLanguageId(language), + title, + ...(isNotEmpty(description) ? { description: convertRTCToEndpointRTC(description) } : {}), + })) ?? [], +}); diff --git a/src/collections/Pages/endpoints/getBySlugEndpoint.ts b/src/collections/Pages/endpoints/getBySlugEndpoint.ts index e8e8619..d085317 100644 --- a/src/collections/Pages/endpoints/getBySlugEndpoint.ts +++ b/src/collections/Pages/endpoints/getBySlugEndpoint.ts @@ -2,15 +2,13 @@ import { BreakBlockType, Collections, PageType, - RichTextBreakBlock, RichTextContent, - RichTextSectionBlock, isBlockNodeBreakBlock, isBlockNodeSectionBlock, isNodeBlockNode, } from "../../../constants"; import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; -import { EndpointPage, EndpointPagePreview, TableOfContentEntry } from "../../../sdk"; +import { EndpointPage, TableOfContentEntry } from "../../../sdk"; import { Page } from "../../../types/collections"; import { isNotEmpty, @@ -18,85 +16,70 @@ import { isPayloadType, isValidPayloadImage, } from "../../../utils/asserts"; -import { convertTagsToGroups, handleParentPages, handleRecorder } from "../../../utils/endpoints"; +import { + convertRTCToEndpointRTC, + convertSourceToEndpointSource, + convertTagsEndpointTagsGroups, +} from "../../../utils/endpoints"; +import { convertRecorderToEndpointRecorder } from "../../Recorders/endpoints/getByUsername"; export const getBySlugEndpoint = createGetByEndpoint({ collection: Collections.Pages, attribute: "slug", - handler: (page: Page): EndpointPage => { - const { translations, collectibles, folders, backgroundImage } = page; - - return { - ...convertPageToPreview(page), - ...(isValidPayloadImage(backgroundImage) ? { backgroundImage } : {}), - translations: translations.map( - ({ - content, - language, - sourceLanguage, - title, - pretitle, - subtitle, - proofreaders, - summary, - transcribers, - translators, - }) => ({ - language: isPayloadType(language) ? language.id : language, - sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage, - ...(isNotEmpty(pretitle) ? { pretitle } : {}), - title, - ...(isNotEmpty(subtitle) ? { subtitle } : {}), - ...(isNotEmpty(summary) ? { summary } : {}), - content: handleContent(content), - toc: handleToc(content), - translators: isPayloadArrayType(translators) ? translators.map(handleRecorder) : [], - transcribers: isPayloadArrayType(transcribers) ? transcribers.map(handleRecorder) : [], - proofreaders: isPayloadArrayType(proofreaders) ? proofreaders.map(handleRecorder) : [], - }) - ), - parentPages: handleParentPages({ collectibles, folders }), - }; - }, + handler: (page) => convertPageToEndpointPage(page), }); -const handleContent = ( - { root: { children, ...others } }: RichTextContent, - parentPrefix = "" -): RichTextContent => { - let index = 0; - return { - root: { - ...others, - children: children.map((node) => { - if (isNodeBlockNode(node)) { - if (isBlockNodeSectionBlock(node)) { - index++; - const anchorHash = `${parentPrefix}${index}.`; - const newNode: RichTextSectionBlock = { - ...node, - fields: { - ...node.fields, - content: handleContent(node.fields.content, anchorHash), - }, - anchorHash, - }; - return newNode; - } else if (isBlockNodeBreakBlock(node)) { - index++; - const anchorHash = `${parentPrefix}${index}.`; - const newNode: RichTextBreakBlock = { - ...node, - anchorHash, - }; - return newNode; - } - } - return node; - }), - }, - }; -}; +export const convertPageToEndpointPage = ({ + translations, + collectibles, + folders, + backgroundImage, + authors, + slug, + tags, + thumbnail, + type, +}: Page): EndpointPage => ({ + slug, + type: type as PageType, + ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), + tagGroups: convertTagsEndpointTagsGroups(tags), + authors: isPayloadArrayType(authors) ? authors.map(convertRecorderToEndpointRecorder) : [], + ...(isValidPayloadImage(backgroundImage) ? { backgroundImage } : {}), + translations: translations.map( + ({ + content, + language, + sourceLanguage, + title, + pretitle, + subtitle, + proofreaders, + summary, + transcribers, + translators, + }) => ({ + language: isPayloadType(language) ? language.id : language, + sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage, + ...(isNotEmpty(pretitle) ? { pretitle } : {}), + title, + ...(isNotEmpty(subtitle) ? { subtitle } : {}), + ...(isNotEmpty(summary) ? { summary } : {}), + content: convertRTCToEndpointRTC(content), + toc: handleToc(content), + translators: isPayloadArrayType(translators) + ? translators.map(convertRecorderToEndpointRecorder) + : [], + transcribers: isPayloadArrayType(transcribers) + ? transcribers.map(convertRecorderToEndpointRecorder) + : [], + proofreaders: isPayloadArrayType(proofreaders) + ? proofreaders.map(convertRecorderToEndpointRecorder) + : [], + }) + ), + parentPages: convertSourceToEndpointSource({ collectibles, folders }), +}); const handleToc = (content: RichTextContent, parentPrefix = ""): TableOfContentEntry[] => { let index = 0; @@ -150,24 +133,3 @@ const handleToc = (content: RichTextContent, parentPrefix = ""): TableOfContentE return []; }); }; - -export const convertPageToPreview = ({ - authors, - slug, - translations, - tags, - thumbnail, - type, -}: Page): EndpointPagePreview => ({ - slug, - type: type as PageType, - ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), - tagGroups: convertTagsToGroups(tags), - translations: translations.map(({ language, title, pretitle, subtitle }) => ({ - language: isPayloadType(language) ? language.id : language, - ...(isNotEmpty(pretitle) ? { pretitle } : {}), - title, - ...(isNotEmpty(subtitle) ? { subtitle } : {}), - })), - authors: isPayloadArrayType(authors) ? authors.map(handleRecorder) : [], -}); diff --git a/src/collections/Recorders/Recorders.ts b/src/collections/Recorders/Recorders.ts index 274ea20..9f00221 100644 --- a/src/collections/Recorders/Recorders.ts +++ b/src/collections/Recorders/Recorders.ts @@ -7,6 +7,7 @@ import { imageField } from "../../fields/imageField/imageField"; import { rowField } from "../../fields/rowField/rowField"; import { buildCollectionConfig } from "../../utils/collectionConfig"; import { getAllEndpoint } from "./endpoints/getAllEndpoint"; +import { getByUsernameEndpoint } from "./endpoints/getByUsername"; import { importFromStrapi } from "./endpoints/importFromStrapi"; import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole"; @@ -74,7 +75,7 @@ export const Recorders = buildCollectionConfig({ hooks: { beforeLogin: [beforeLoginMustHaveAtLeastOneRole], }, - endpoints: [importFromStrapi, getAllEndpoint], + endpoints: [importFromStrapi, getAllEndpoint, getByUsernameEndpoint], timestamps: false, fields: [ rowField([ diff --git a/src/collections/Recorders/endpoints/getByUsername.ts b/src/collections/Recorders/endpoints/getByUsername.ts new file mode 100644 index 0000000..69005a0 --- /dev/null +++ b/src/collections/Recorders/endpoints/getByUsername.ts @@ -0,0 +1,24 @@ +import { Collections } from "../../../constants"; +import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; +import { EndpointRecorder } from "../../../sdk"; +import { Recorder } from "../../../types/collections"; +import { isPayloadType, isValidPayloadImage } from "../../../utils/asserts"; + +export const getByUsernameEndpoint = createGetByEndpoint({ + collection: Collections.Recorders, + attribute: "username", + handler: (recorder) => convertRecorderToEndpointRecorder(recorder), +}); + +export const convertRecorderToEndpointRecorder = ({ + id, + languages, + username, + avatar, + anonymize, +}: Recorder): EndpointRecorder => ({ + id, + languages: languages?.map((language) => (isPayloadType(language) ? language.id : language)) ?? [], + username: anonymize ? `Recorder#${id.substring(0, 5)}` : username, + ...(isValidPayloadImage(avatar) ? { avatar } : {}), +}); diff --git a/src/collections/Videos/Videos.ts b/src/collections/Videos/Videos.ts index 6fd32f0..19f11ef 100644 --- a/src/collections/Videos/Videos.ts +++ b/src/collections/Videos/Videos.ts @@ -6,6 +6,7 @@ import { tagsField } from "../../fields/tagsField/tagsField"; import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { buildCollectionConfig } from "../../utils/collectionConfig"; import { createEditor } from "../../utils/editor"; +import { getByID } from "./endpoints/getByID"; const fields = { filename: "filename", @@ -47,13 +48,13 @@ export const Videos = buildCollectionConfig({ mimeTypes: ["video/*"], disableLocalStorage: true, }, + endpoints: [getByID], fields: [ rowField([ { name: fields.duration, type: "number", min: 0, required: true }, imageField({ name: fields.thumbnail, relationTo: Collections.MediaThumbnails, - required: true, }), ]), translatedFields({ diff --git a/src/collections/Videos/endpoints/getByID.ts b/src/collections/Videos/endpoints/getByID.ts new file mode 100644 index 0000000..d61ba6b --- /dev/null +++ b/src/collections/Videos/endpoints/getByID.ts @@ -0,0 +1,108 @@ +import payload from "payload"; +import { Collections } from "../../../constants"; +import { EndpointVideo, PayloadMedia } from "../../../sdk"; +import { Video } from "../../../types/collections"; +import { CollectionEndpoint } from "../../../types/payload"; +import { + isDefined, + isEmpty, + isNotEmpty, + isPayloadType, + isUndefined, + isValidPayloadImage, + isValidPayloadMedia, +} from "../../../utils/asserts"; +import { + convertRTCToEndpointRTC, + convertTagsEndpointTagsGroups, + getLanguageId, +} from "../../../utils/endpoints"; + +export const getByID: CollectionEndpoint = { + method: "get", + path: "/id/:id", + handler: async (req, res) => { + if (!req.user) { + return res.status(403).send({ + errors: [ + { + message: "You are not allowed to perform this action.", + }, + ], + }); + } + + if (!req.params.id) { + return res.status(400).send({ errors: [{ message: "Missing 'id' query params" }] }); + } + + try { + const result = await payload.findByID({ + collection: Collections.Videos, + id: req.params.id, + }); + + if (!isValidPayloadMedia(result)) { + return res.sendStatus(404); + } + + return res.status(200).json(convertVideoToEndpointVideo(result)); + } catch { + return res.sendStatus(404); + } + }, +}; + +export const convertVideoToEndpointVideo = ({ + url, + tags, + translations, + mimeType, + createdAt, + updatedAt, + filename, + filesize, + duration, + id, + thumbnail, + platform, + platformEnabled, +}: Video & PayloadMedia): EndpointVideo => ({ + url, + tagGroups: convertTagsEndpointTagsGroups(tags), + createdAt, + filename, + filesize, + id, + mimeType, + updatedAt, + translations: + translations?.map(({ language, title, description }) => ({ + language: getLanguageId(language), + title, + ...(isNotEmpty(description) ? { description: convertRTCToEndpointRTC(description) } : {}), + })) ?? [], + + duration, + ...(isValidPayloadImage(thumbnail) ? { thumbnail } : {}), + ...(platformEnabled && isDefined(platform) && isPayloadType(platform.channel) + ? { + platform: { + channel: platform.channel, + publishedDate: platform.publishedDate, + url: platform.url, + }, + } + : {}), + subtitles: + translations.flatMap(({ language, subfile }) => { + if ( + isUndefined(subfile) || + !isPayloadType(subfile) || + isUndefined(subfile.url) || + isEmpty(subfile.url) + ) + return []; + return { language: getLanguageId(language), url: subfile.url }; + }) ?? [], +}); diff --git a/src/collections/VideosChannels/VideosChannels.ts b/src/collections/VideosChannels/VideosChannels.ts index 227f2b0..d3bd118 100644 --- a/src/collections/VideosChannels/VideosChannels.ts +++ b/src/collections/VideosChannels/VideosChannels.ts @@ -29,7 +29,7 @@ export const VideosChannels: CollectionConfig = buildCollectionConfig({ { name: fields.url, type: "text", required: true, unique: true }, rowField([ { name: fields.title, type: "text", required: true }, - { name: fields.subscribers, type: "number" }, + { name: fields.subscribers, type: "number", required: true }, ]), backPropagationField({ name: fields.videos, diff --git a/src/collections/WebsiteConfig/endpoints/getConfigEndpoint.ts b/src/collections/WebsiteConfig/endpoints/getConfigEndpoint.ts index b0cb6a5..955a786 100644 --- a/src/collections/WebsiteConfig/endpoints/getConfigEndpoint.ts +++ b/src/collections/WebsiteConfig/endpoints/getConfigEndpoint.ts @@ -3,7 +3,7 @@ import { Collections } from "../../../constants"; import { EndpointWebsiteConfig } from "../../../sdk"; import { CollectionEndpoint } from "../../../types/payload"; import { isPayloadType, isValidPayloadImage } from "../../../utils/asserts"; -import { convertFolderToPreview } from "../../Folders/endpoints/getBySlugEndpoint"; +import { convertFolderToEndpointFolder } from "../../Folders/endpoints/getBySlugEndpoint"; export const getConfigEndpoint: CollectionEndpoint = { method: "get", @@ -46,7 +46,7 @@ export const getConfigEndpoint: CollectionEndpoint = { homeFolders?.flatMap(({ folder, darkThumbnail, lightThumbnail }) => { if (!isPayloadType(folder)) return []; return { - ...convertFolderToPreview(folder), + ...convertFolderToEndpointFolder(folder), ...(isValidPayloadImage(darkThumbnail) ? { darkThumbnail } : {}), ...(isValidPayloadImage(lightThumbnail) ? { lightThumbnail } : {}), }; diff --git a/src/constants.ts b/src/constants.ts index 06e091f..0fd48d4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,11 +1,5 @@ -import type { - Audio, - BreakBlock, - Image, - SectionBlock, - TranscriptBlock, - Video, -} from "./types/collections"; +import { EndpointAudio, EndpointImage, EndpointVideo } from "./sdk"; +import type { BreakBlock, SectionBlock, TranscriptBlock } from "./types/collections"; // END MOCKING SECTION @@ -149,17 +143,17 @@ export interface RichTextUploadNode extends RichTextNode { export interface RichTextUploadImageNode extends RichTextUploadNode { relationTo: Collections.Images; - value: Image; + value: EndpointImage; } export interface RichTextUploadVideoNode extends RichTextUploadNode { relationTo: Collections.Videos; - value: Video; + value: EndpointVideo; } export interface RichTextUploadAudioNode extends RichTextUploadNode { relationTo: Collections.Audios; - value: Audio; + value: EndpointAudio; } export interface RichTextTextNode extends RichTextNode { diff --git a/src/payload.config.ts b/src/payload.config.ts index fc0e459..8961db4 100644 --- a/src/payload.config.ts +++ b/src/payload.config.ts @@ -35,7 +35,7 @@ const configuredFtpAdapter = sftpAdapter({ privateKey: process.env.SFTP_PRIVATE_KEY, }, destinationPathRoot: process.env.SFTP_DESTINATION_PATH_ROOT ?? "", - publicEndpoint: process.env.FTP_BASE_URL ?? "", + publicEndpoint: process.env.SFTP_BASE_URL ?? "", }); export default buildConfig({ diff --git a/src/sdk.ts b/src/sdk.ts index 023c98c..0993181 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -96,7 +96,7 @@ const request = async (url: string, init?: RequestInit): Promise => { // SDK and Types -export type EndpointFolderPreview = { +export type EndpointFolder = { slug: string; icon?: string; translations: { @@ -104,33 +104,42 @@ export type EndpointFolderPreview = { name: string; description?: RichTextContent; }[]; -}; - -export type EndpointFolder = EndpointFolderPreview & { sections: - | { type: "single"; subfolders: EndpointFolderPreview[] } + | { type: "single"; subfolders: EndpointFolder[] } | { type: "multiple"; sections: { translations: { language: string; name: string }[]; - subfolders: EndpointFolderPreview[]; + subfolders: EndpointFolder[]; }[]; }; files: ( | { - relationTo: "collectibles"; - value: EndpointCollectiblePreview; + relationTo: Collections.Collectibles; + value: EndpointCollectible; } | { - relationTo: "pages"; - value: EndpointPagePreview; + relationTo: Collections.Pages; + value: EndpointPage; + } + | { + relationTo: Collections.Images; + value: EndpointImage; + } + | { + relationTo: Collections.Audios; + value: EndpointAudio; + } + | { + relationTo: Collections.Videos; + value: EndpointVideo; } )[]; parentPages: EndpointSource[]; }; export type EndpointWebsiteConfig = { - homeFolders: (EndpointFolderPreview & { + homeFolders: (EndpointFolder & { lightThumbnail?: PayloadImage; darkThumbnail?: PayloadImage; })[]; @@ -178,23 +187,18 @@ export type EndpointTagsGroup = { tags: EndpointTag[]; }; -export type EndpointPagePreview = { +export type EndpointPage = { slug: string; type: PageType; thumbnail?: PayloadImage; authors: EndpointRecorder[]; tagGroups: EndpointTagsGroup[]; + backgroundImage?: PayloadImage; translations: { language: string; pretitle?: string; title: string; subtitle?: string; - }[]; -}; - -export type EndpointPage = EndpointPagePreview & { - backgroundImage?: PayloadImage; - translations: (EndpointPagePreview["translations"][number] & { sourceLanguage: string; summary?: RichTextContent; content: RichTextContent; @@ -202,11 +206,11 @@ export type EndpointPage = EndpointPagePreview & { translators: EndpointRecorder[]; proofreaders: EndpointRecorder[]; toc: TableOfContentEntry[]; - })[]; + }[]; parentPages: EndpointSource[]; }; -export type EndpointCollectiblePreview = { +export type EndpointCollectible = { slug: string; thumbnail?: PayloadImage; translations: { @@ -219,9 +223,6 @@ export type EndpointCollectiblePreview = { tagGroups: EndpointTagsGroup[]; releaseDate?: string; languages: string[]; -}; - -export type EndpointCollectible = EndpointCollectiblePreview & { backgroundImage?: PayloadImage; nature: CollectibleNature; gallery: PayloadImage[]; @@ -242,15 +243,23 @@ export type EndpointCollectible = EndpointCollectiblePreview & { bindingType?: CollectibleBindingTypes; pageOrder?: CollectiblePageOrders; }; - subitems: EndpointCollectiblePreview[]; + subitems: EndpointCollectible[]; contents: { content: | { - relationTo: "pages"; - value: EndpointPagePreview; + relationTo: Collections.Pages; + value: EndpointPage; } | { - relationTo: "generic-contents"; + relationTo: Collections.Audios; + value: EndpointAudio; + } + | { + relationTo: Collections.Videos; + value: EndpointVideo; + } + | { + relationTo: Collections.GenericContents; value: { translations: { language: string; @@ -315,21 +324,72 @@ export type EndpointSource = | { type: "url"; url: string; label: string } | { type: "collectible"; - collectible: EndpointCollectiblePreview; + collectible: EndpointCollectible; range?: | { type: "page"; page: number } | { type: "timestamp"; timestamp: string } | { type: "custom"; translations: { language: string; note: string }[] }; } - | { type: "page"; page: EndpointPagePreview } - | { type: "folder"; folder: EndpointFolderPreview }; + | { type: "page"; page: EndpointPage } + | { type: "folder"; folder: EndpointFolder }; -export type PayloadImage = { +export type EndpointMedia = { + id: string; url: string; + filename: string; + mimeType: string; + filesize: number; + updatedAt: string; + createdAt: string; + tagGroups: EndpointTagsGroup[]; + translations: { + language: string; + title: string; + description?: RichTextContent; + }[]; +}; + +export type EndpointImage = EndpointMedia & { width: number; height: number; +}; + +export type EndpointAudio = EndpointMedia & { + thumbnail?: PayloadImage; + duration: number; +}; + +export type EndpointVideo = EndpointMedia & { + thumbnail?: PayloadImage; + subtitles: { + language: string; + url: string; + }[]; + platform?: { + channel: { + url: string; + title: string; + subscribers: number; + }; + views?: number; + likes?: number; + dislikes?: number; + url: string; + publishedDate: string; + }; + duration: number; +}; + +export type PayloadMedia = { + url: string; mimeType: string; filename: string; + filesize: number; +}; + +export type PayloadImage = PayloadMedia & { + width: number; + height: number; }; export const payload = { @@ -353,4 +413,10 @@ export const payload = { 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(), }; diff --git a/src/types/collections.ts b/src/types/collections.ts index a2e2494..541b7a2 100644 --- a/src/types/collections.ts +++ b/src/types/collections.ts @@ -603,7 +603,7 @@ export interface MediaThumbnail { export interface Video { id: string; duration: number; - thumbnail: string | MediaThumbnail; + thumbnail?: string | MediaThumbnail | null; translations: { language: string | Language; title: string; @@ -665,7 +665,7 @@ export interface VideosChannel { id: string; url: string; title: string; - subscribers?: number | null; + subscribers: number; videos?: (string | Video)[] | null; } /** diff --git a/src/utils/asserts.ts b/src/utils/asserts.ts index 57953fa..5e62f81 100644 --- a/src/utils/asserts.ts +++ b/src/utils/asserts.ts @@ -1,5 +1,5 @@ import { RichTextContent, isNodeParagraphNode } from "../constants"; -import { PayloadImage } from "../sdk"; +import { PayloadImage, PayloadMedia } from "../sdk"; export const isDefined = (value: T | null | undefined): value is T => value !== null && value !== undefined; @@ -7,10 +7,11 @@ export const isDefined = (value: T | null | undefined): value is T => export const isUndefined = (value: T | null | undefined): value is null | undefined => !isDefined(value); -export const isNotEmpty = (value: string | null | undefined | RichTextContent): value is string => - !isEmpty(value); +export const isNotEmpty = ( + value: string | null | undefined | RichTextContent +): value is string | RichTextContent => !isEmpty(value); -export const isEmpty = (value: string | null | undefined | RichTextContent): value is string => +export const isEmpty = (value: string | null | undefined | RichTextContent): boolean => isUndefined(value) || (typeof value === "string" && isEmptyString(value)) || (typeof value === "object" && isEmptyRichText(value)); @@ -26,6 +27,7 @@ export const isValidPayloadImage = ( image: | { filename?: string | null; + filesize?: number | null; mimeType?: string | null; width?: number | null; height?: number | null; @@ -42,6 +44,28 @@ export const isValidPayloadImage = ( if (isEmpty(image.mimeType)) return false; if (isUndefined(image.width)) return false; if (isUndefined(image.height)) return false; + if (isUndefined(image.filesize)) return false; + return true; +}; + +export const isValidPayloadMedia = ( + media: + | { + filename?: string | null; + filesize?: number | null; + mimeType?: string | null; + url?: string | null; + } + | undefined + | null + | string +): media is PayloadMedia => { + if (isUndefined(media)) return false; + if (typeof media === "string") return false; + if (isEmpty(media.filename)) return false; + if (isEmpty(media.url)) return false; + if (isEmpty(media.mimeType)) return false; + if (isUndefined(media.filesize)) return false; return true; }; diff --git a/src/utils/endpoints.ts b/src/utils/endpoints.ts index 13b44ad..5ef5a32 100644 --- a/src/utils/endpoints.ts +++ b/src/utils/endpoints.ts @@ -1,10 +1,32 @@ -import { convertCollectibleToPreview } from "../collections/Collectibles/endpoints/getBySlugEndpoint"; -import { convertFolderToPreview } from "../collections/Folders/endpoints/getBySlugEndpoint"; -import { EndpointRecorder, EndpointSource, EndpointTag, EndpointTagsGroup } from "../sdk"; -import { Collectible, Folder, Recorder, Tag } from "../types/collections"; -import { isPayloadArrayType, isPayloadType, isPublished, isValidPayloadImage } from "./asserts"; +import { convertAudioToEndpointAudio } from "../collections/Audios/endpoints/getByID"; +import { convertCollectibleToEndpointCollectible } from "../collections/Collectibles/endpoints/getBySlugEndpoint"; +import { convertFolderToEndpointFolder } from "../collections/Folders/endpoints/getBySlugEndpoint"; +import { convertImageToEndpointImage } from "../collections/Images/endpoints/getByID"; +import { convertVideoToEndpointVideo } from "../collections/Videos/endpoints/getByID"; +import { + RichTextBreakBlock, + RichTextContent, + RichTextSectionBlock, + RichTextUploadNode, + isBlockNodeBreakBlock, + isBlockNodeSectionBlock, + isNodeBlockNode, + isNodeUploadNode, + isUploadNodeAudioNode, + isUploadNodeImageNode, + isUploadNodeVideoNode, +} from "../constants"; +import { EndpointSource, EndpointTag, EndpointTagsGroup } from "../sdk"; +import { Audio, Collectible, Folder, Image, Language, Tag, Video } from "../types/collections"; +import { + isPayloadArrayType, + isPayloadType, + isPublished, + isValidPayloadImage, + isValidPayloadMedia, +} from "./asserts"; -export const convertTagsToGroups = ( +export const convertTagsEndpointTagsGroups = ( tags: (string | Tag)[] | null | undefined ): EndpointTagsGroup[] => { if (!isPayloadArrayType(tags)) { @@ -44,7 +66,75 @@ export const convertTagsToGroups = ( return groups; }; -export const handleParentPages = ({ +export const convertRTCToEndpointRTC = ( + { root: { children, ...others } }: RichTextContent, + parentPrefix = "" +): RichTextContent => { + let index = 0; + return { + root: { + ...others, + children: children.map((node) => { + if (isNodeBlockNode(node)) { + // Add anchor hash on section block (TOC) + if (isBlockNodeSectionBlock(node)) { + index++; + const anchorHash = `${parentPrefix}${index}.`; + const newNode: RichTextSectionBlock = { + ...node, + fields: { + ...node.fields, + content: convertRTCToEndpointRTC(node.fields.content, anchorHash), + }, + anchorHash, + }; + return newNode; + // Add anchor hash on section block (TOC) + } else if (isBlockNodeBreakBlock(node)) { + index++; + const anchorHash = `${parentPrefix}${index}.`; + const newNode: RichTextBreakBlock = { + ...node, + anchorHash, + }; + return newNode; + } + } else if (isNodeUploadNode(node)) { + const errorUploadNode: RichTextUploadNode = { + type: "upload", + relationTo: "error", + version: 1, + }; + if (isUploadNodeImageNode(node)) { + const value = node.value as Image | string; + if (!isPayloadType(value) || !isValidPayloadImage(value)) return errorUploadNode; + return { + ...node, + value: convertImageToEndpointImage(value), + }; + } else if (isUploadNodeAudioNode(node)) { + const value = node.value as Audio | string; + if (!isPayloadType(value) || !isValidPayloadMedia(value)) return errorUploadNode; + return { + ...node, + value: convertAudioToEndpointAudio(value), + }; + } else if (isUploadNodeVideoNode(node)) { + const value = node.value as Video | string; + if (!isPayloadType(value) || !isValidPayloadMedia(value)) return errorUploadNode; + return { + ...node, + value: convertVideoToEndpointVideo(value), + }; + } + } + return node; + }), + }, + }; +}; + +export const convertSourceToEndpointSource = ({ collectibles, folders, }: { @@ -57,7 +147,7 @@ export const handleParentPages = ({ collectibles.filter(isPublished).forEach((collectible) => { result.push({ type: "collectible", - collectible: convertCollectibleToPreview(collectible), + collectible: convertCollectibleToEndpointCollectible(collectible), }); }); } @@ -66,7 +156,7 @@ export const handleParentPages = ({ folders.forEach((folder) => { result.push({ type: "folder", - folder: convertFolderToPreview(folder), + folder: convertFolderToEndpointFolder(folder), }); }); } @@ -74,19 +164,6 @@ export const handleParentPages = ({ return result; }; -export const handleRecorder = ({ - id, - languages, - username, - avatar, - anonymize, -}: Recorder): EndpointRecorder => ({ - id, - languages: languages?.map((language) => (isPayloadType(language) ? language.id : language)) ?? [], - username: anonymize ? `Recorder#${id.substring(0, 5)}` : username, - ...(isValidPayloadImage(avatar) ? { avatar } : {}), -}); - export const getDomainFromUrl = (url: string): string => { const urlObject = new URL(url); let domain = urlObject.hostname; @@ -95,3 +172,6 @@ export const getDomainFromUrl = (url: string): string => { } return domain; }; + +export const getLanguageId = (language: string | Language) => + typeof language === "object" ? language.id : language;