Move from using parentPages to backlinks

This commit is contained in:
DrMint 2024-07-21 10:24:14 +02:00
parent 8cd8a67778
commit 5acaf65ade
31 changed files with 555 additions and 566 deletions

24
package-lock.json generated
View File

@ -10,7 +10,7 @@
"license": "MIT",
"dependencies": {
"@fontsource/vollkorn": "5.0.20",
"@iconify-json/material-symbols": "^1.1.84",
"@iconify-json/material-symbols": "^1.1.85",
"@payloadcms/bundler-webpack": "1.0.7",
"@payloadcms/db-mongodb": "1.6.0",
"@payloadcms/richtext-lexical": "0.11.2",
@ -19,9 +19,10 @@
"language-tags": "1.0.9",
"luxon": "3.4.4",
"payload": "2.24.0",
"payloadcms-relationships": "github:DrMint/payloadcms-relationships",
"payloadcms-sftp-storage": "1.0.1",
"sharp": "0.33.4",
"styled-components": "6.1.11"
"styled-components": "6.1.12"
},
"devDependencies": {
"@types/express": "4.17.21",
@ -1962,9 +1963,9 @@
}
},
"node_modules/@iconify-json/material-symbols": {
"version": "1.1.84",
"resolved": "https://registry.npmjs.org/@iconify-json/material-symbols/-/material-symbols-1.1.84.tgz",
"integrity": "sha512-Vio0Ns2rzdTsushHGBiyAcIIbGNWXI6nTNsrVqcq1EEPkfNkuchufBMvErs9vdc1vN1Fj7Ul3uWA3r988TIvfQ==",
"version": "1.1.85",
"resolved": "https://registry.npmjs.org/@iconify-json/material-symbols/-/material-symbols-1.1.85.tgz",
"integrity": "sha512-GJXTScAIdaxxMPcp6GCd4qbntvHpG9UrF/2V03PMUuD7+1fMU5vHG5w0IGDdvqOnI9HpEcUFa7CFDVQHOpBeDA==",
"dependencies": {
"@iconify/types": "*"
}
@ -9055,6 +9056,13 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/payloadcms-relationships": {
"version": "1.0.0",
"resolved": "git+ssh://git@github.com/DrMint/payloadcms-relationships.git#aa65c94f14fa36abe1b482a56fd82d4df3cbfb3e",
"dependencies": {
"payload": "^2.24.0"
}
},
"node_modules/payloadcms-sftp-storage": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/payloadcms-sftp-storage/-/payloadcms-sftp-storage-1.0.1.tgz",
@ -11545,9 +11553,9 @@
}
},
"node_modules/styled-components": {
"version": "6.1.11",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.11.tgz",
"integrity": "sha512-Ui0jXPzbp1phYij90h12ksljKGqF8ncGx+pjrNPsSPhbUUjWT2tD1FwGo2LF6USCnbrsIhNngDfodhxbegfEOA==",
"version": "6.1.12",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.12.tgz",
"integrity": "sha512-n/O4PzRPhbYI0k1vKKayfti3C/IGcPf+DqcrOB7O/ab9x4u/zjqraneT5N45+sIe87cxrCApXM8Bna7NYxwoTA==",
"dependencies": {
"@emotion/is-prop-valid": "1.2.2",
"@emotion/unitless": "0.8.1",

View File

@ -22,7 +22,7 @@
},
"dependencies": {
"@fontsource/vollkorn": "5.0.20",
"@iconify-json/material-symbols": "^1.1.84",
"@iconify-json/material-symbols": "^1.1.85",
"@payloadcms/bundler-webpack": "1.0.7",
"@payloadcms/db-mongodb": "1.6.0",
"@payloadcms/richtext-lexical": "0.11.2",
@ -31,9 +31,10 @@
"language-tags": "1.0.9",
"luxon": "3.4.4",
"payload": "2.24.0",
"payloadcms-relationships": "github:DrMint/payloadcms-relationships",
"payloadcms-sftp-storage": "1.0.1",
"sharp": "0.33.4",
"styled-components": "6.1.11"
"styled-components": "6.1.12"
},
"devDependencies": {
"@types/express": "4.17.21",

View File

@ -7,6 +7,7 @@ import {
convertCreditsToEndpointCredits,
convertMediaThumbnailToEndpointPayloadImage,
convertRTCToEndpointRTC,
convertRelationshipsToEndpointRelations,
getLanguageId,
} from "../../../utils/endpoints";
import { Collections } from "../../../shared/payload/constants";
@ -15,6 +16,7 @@ import {
EndpointAudioPreview,
EndpointAudio,
} from "../../../shared/payload/endpoint-types";
import { findIncomingRelationships } from "payloadcms-relationships";
export const getByID: CollectionEndpoint = {
method: "get",
@ -44,7 +46,7 @@ export const getByID: CollectionEndpoint = {
return res.sendStatus(404);
}
return res.status(200).json(convertAudioToEndpointAudio(result));
return res.status(200).json(await convertAudioToEndpointAudio(result));
} catch {
return res.sendStatus(404);
}
@ -79,8 +81,8 @@ export const convertAudioToEndpointAudioPreview = ({
: {}),
});
const convertAudioToEndpointAudio = (audio: Audio & PayloadMedia): EndpointAudio => {
const { translations, createdAt, updatedAt, filesize, credits } = audio;
const convertAudioToEndpointAudio = async (audio: Audio & PayloadMedia): Promise<EndpointAudio> => {
const { translations, createdAt, updatedAt, filesize, credits, id } = audio;
return {
...convertAudioToEndpointAudioPreview(audio),
createdAt,
@ -95,5 +97,8 @@ const convertAudioToEndpointAudio = (audio: Audio & PayloadMedia): EndpointAudio
...(isNotEmpty(description) ? { description: convertRTCToEndpointRTC(description) } : {}),
})) ?? [],
credits: convertCreditsToEndpointCredits(credits),
backlinks: convertRelationshipsToEndpointRelations(
await findIncomingRelationships(Collections.Audios, id)
),
};
};

View File

@ -6,7 +6,11 @@ import { convertCreditsToEndpointCredits, getDomainFromUrl } from "../../../util
import { convertCollectibleToEndpointCollectiblePreview } from "../../Collectibles/endpoints/getBySlugEndpoint";
import { convertPageToEndpointPagePreview } from "../../Pages/endpoints/getBySlugEndpoint";
import { Collections } from "../../../shared/payload/constants";
import { EndpointChronologyEvent, EndpointSource } from "../../../shared/payload/endpoint-types";
import {
EndpointChronologyEvent,
EndpointCollectibleRelationRange,
EndpointRelation,
} from "../../../shared/payload/endpoint-types";
export const getAllEndpoint: CollectionEndpoint = {
method: "get",
@ -48,13 +52,13 @@ export const getAllEndpoint: CollectionEndpoint = {
if (aDay !== bDay) return aDay - bDay;
return 0;
})
.map<EndpointChronologyEvent>(eventToEndpointEvent);
.map<EndpointChronologyEvent>(convertEventToEndpointEvent);
res.status(200).json(events);
},
};
export const eventToEndpointEvent = ({
export const convertEventToEndpointEvent = ({
date: { year, day, month },
events,
id,
@ -80,24 +84,26 @@ export const eventToEndpointEvent = ({
})),
});
const handleSources = (sources: ChronologyEvent["events"][number]["sources"]): EndpointSource[] => {
const handleSources = (
sources: ChronologyEvent["events"][number]["sources"]
): EndpointRelation[] => {
return (
sources?.flatMap<EndpointSource>((source) => {
sources?.flatMap<EndpointRelation>((source) => {
switch (source.blockType) {
case "collectibleBlock":
const range = handleRange(source.range);
if (!isPayloadType(source.collectible)) return [];
return {
type: "collectible",
collectible: convertCollectibleToEndpointCollectiblePreview(source.collectible),
type: Collections.Collectibles,
value: convertCollectibleToEndpointCollectiblePreview(source.collectible),
...(isDefined(range) ? { range } : {}),
};
case "pageBlock":
if (!isPayloadType(source.page)) return [];
return {
type: "page",
page: convertPageToEndpointPagePreview(source.page),
type: Collections.Pages,
value: convertPageToEndpointPagePreview(source.page),
};
case "urlBlock":
@ -113,7 +119,7 @@ const handleSources = (sources: ChronologyEvent["events"][number]["sources"]): E
const handleRange = (
rawRange: CollectibleBlock["range"]
): Extract<EndpointSource, { type: "collectible" }>["range"] => {
): EndpointCollectibleRelationRange | undefined => {
const range = rawRange?.[0];
switch (range?.blockType) {

View File

@ -1,6 +1,6 @@
import payload from "payload";
import { CollectionEndpoint } from "../../../types/payload";
import { eventToEndpointEvent } from "./getAllEndpoint";
import { convertEventToEndpointEvent } from "./getAllEndpoint";
import { Collections } from "../../../shared/payload/constants";
export const getByID: CollectionEndpoint = {
@ -27,7 +27,7 @@ export const getByID: CollectionEndpoint = {
id: req.params.id,
});
return res.status(200).json(eventToEndpointEvent(result));
return res.status(200).json(convertEventToEndpointEvent(result));
} catch {
return res.sendStatus(404);
}

View File

@ -1,7 +1,5 @@
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
import { Where } from "payload/types";
import { attributesField } from "../../fields/attributesField/attributesField";
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
import { componentField } from "../../fields/componentField/componentField";
import { creditsField } from "../../fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField";
@ -11,8 +9,6 @@ import { translatedFields } from "../../fields/translatedFields/translatedFields
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
import { Collectible } from "../../types/collections";
import { isPayloadType } from "../../utils/asserts";
import { createEditor } from "../../utils/editor";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { RowLabel } from "./components/RowLabel";
@ -695,42 +691,9 @@ export const Collectibles = buildVersionedCollectionConfig({
},
],
},
rowField([
backPropagationField({
name: fields.folders,
relationTo: Collections.Folders,
hasMany: true,
where: ({ id }) => ({
and: [
{ "files.value": { equals: id } },
{ "files.relationTo": { equals: Collections.Collectibles } },
] as Where[],
}),
admin: {
description: `You can go to the "Folders" collection to include this collectible in a folder.`,
},
}),
backPropagationField({
name: fields.parentItems,
relationTo: Collections.Collectibles,
hasMany: true,
where: ({ id }) => ({ [fields.subitems]: { equals: id } }),
}),
]),
],
},
],
},
],
custom: {
getBackPropagatedRelationships: ({ subitems, contents }: Collectible) => {
const result: string[] = [];
subitems?.forEach((subitem) => result.push(isPayloadType(subitem) ? subitem.id : subitem));
contents?.forEach(({ content: { relationTo, value } }) => {
if (relationTo === "pages") result.push(isPayloadType(value) ? value.id : value);
});
return result;
},
},
});

View File

@ -20,8 +20,8 @@ import {
import {
convertAttributesToEndpointAttributes,
convertImageToEndpointPayloadImage,
convertRelationshipsToEndpointRelations,
convertScanToEndpointScanImage,
convertSourceToEndpointSource,
getDomainFromUrl,
} from "../../../utils/endpoints";
import { convertAudioToEndpointAudioPreview } from "../../Audios/endpoints/getByID";
@ -29,12 +29,13 @@ import { convertFileToEndpointFilePreview } from "../../Files/endpoints/getByID"
import { convertPageToEndpointPagePreview } from "../../Pages/endpoints/getBySlugEndpoint";
import { convertRecorderToEndpointRecorderPreview } from "../../Recorders/endpoints/getByID";
import { convertVideoToEndpointVideoPreview } from "../../Videos/endpoints/getByID";
import { findIncomingRelationships } from "payloadcms-relationships";
export const getBySlugEndpoint = createGetByEndpoint({
collection: Collections.Collectibles,
attribute: "slug",
depth: 3,
handler: (collectible) => convertCollectibleToEndpointCollectible(collectible),
handler: async (collectible) => await convertCollectibleToEndpointCollectible(collectible),
});
export const convertCollectibleToEndpointCollectiblePreview = ({
@ -64,8 +65,11 @@ export const convertCollectibleToEndpointCollectiblePreview = ({
...handlePrice(price, priceEnabled),
});
const convertCollectibleToEndpointCollectible = (collectible: Collectible): EndpointCollectible => {
const convertCollectibleToEndpointCollectible = async (
collectible: Collectible
): Promise<EndpointCollectible> => {
const {
id,
nature,
urls,
subitems,
@ -80,8 +84,6 @@ const convertCollectibleToEndpointCollectible = (collectible: Collectible): Endp
weightEnabled,
pageInfo,
pageInfoEnabled,
parentItems,
folders,
backgroundImage,
translations,
scans: rawScans,
@ -128,7 +130,9 @@ const convertCollectibleToEndpointCollectible = (collectible: Collectible): Endp
...(isPayloadType(updatedBy)
? { updatedBy: convertRecorderToEndpointRecorderPreview(updatedBy) }
: {}),
parentPages: convertSourceToEndpointSource({ collectibles: parentItems, folders }),
backlinks: convertRelationshipsToEndpointRelations(
await findIncomingRelationships(Collections.Collectibles, id)
),
};
};

View File

@ -2,10 +2,8 @@ import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint";
import { Collections } from "../../../shared/payload/constants";
import { EndpointCollectibleGallery } from "../../../shared/payload/endpoint-types";
import { isImage, isNotEmpty, isPayloadType } from "../../../utils/asserts";
import {
convertImageToEndpointPayloadImage,
convertSourceToEndpointSource,
} from "../../../utils/endpoints";
import { convertImageToEndpointPayloadImage } from "../../../utils/endpoints";
import { convertCollectibleToEndpointCollectiblePreview } from "./getBySlugEndpoint";
export const getBySlugEndpointGallery = createGetByEndpoint({
collection: Collections.Collectibles,
@ -29,7 +27,12 @@ export const getBySlugEndpointGallery = createGetByEndpoint({
gallery?.flatMap(({ image }) =>
isImage(image) ? convertImageToEndpointPayloadImage(image) : []
) ?? [],
parentPages: convertSourceToEndpointSource({ collectibles: [collectible] }),
backlinks: [
{
type: Collections.Collectibles,
value: convertCollectibleToEndpointCollectiblePreview(collectible),
},
],
};
},
});

View File

@ -2,10 +2,10 @@ import payload from "payload";
import { Collectible, Image } from "../../../types/collections";
import { CollectionEndpoint } from "../../../types/payload";
import { isDefined, isImage, isNotEmpty, isPayloadType } from "../../../utils/asserts";
import { convertSourceToEndpointSource } from "../../../utils/endpoints";
import { convertImageToEndpointImage } from "../../Images/endpoints/getByID";
import { Collections } from "../../../shared/payload/constants";
import { EndpointCollectibleGalleryImage } from "../../../shared/payload/endpoint-types";
import { convertCollectibleToEndpointCollectiblePreview } from "./getBySlugEndpoint";
export const getBySlugEndpointGalleryImage: CollectionEndpoint = {
path: "/slug/:slug/gallery/:index",
@ -51,8 +51,7 @@ export const getBySlugEndpointGalleryImage: CollectionEndpoint = {
const nextIndex = getNextIndex(index, collectible.gallery);
const scanPage: EndpointCollectibleGalleryImage = {
image: convertImageToEndpointImage(image),
parentPages: convertSourceToEndpointSource({ gallery: [collectible] }),
image: await convertImageToEndpointImage(image),
slug,
translations:
collectible.translations?.map(({ language, title, description, pretitle, subtitle }) => ({
@ -64,6 +63,12 @@ export const getBySlugEndpointGalleryImage: CollectionEndpoint = {
})) ?? [],
...(isDefined(previousIndex) ? { previousIndex } : {}),
...(isDefined(nextIndex) ? { nextIndex } : {}),
backlinks: [
{
type: Collections.Collectibles,
value: convertCollectibleToEndpointCollectiblePreview(collectible),
},
],
};
res.status(200).send(scanPage);

View File

@ -2,12 +2,10 @@ import payload from "payload";
import { Collectible, Scan } from "../../../types/collections";
import { CollectionEndpoint } from "../../../types/payload";
import { isDefined, isNotEmpty, isPayloadType, isScan } from "../../../utils/asserts";
import {
convertScanToEndpointScanImage,
convertSourceToEndpointSource,
} from "../../../utils/endpoints";
import { convertScanToEndpointScanImage } from "../../../utils/endpoints";
import { Collections } from "../../../shared/payload/constants";
import { EndpointCollectibleScanPage } from "../../../shared/payload/endpoint-types";
import { convertCollectibleToEndpointCollectiblePreview } from "./getBySlugEndpoint";
export const getBySlugEndpointScanPage: CollectionEndpoint = {
path: "/slug/:slug/scans/:index",
@ -54,7 +52,6 @@ export const getBySlugEndpointScanPage: CollectionEndpoint = {
const scanPage: EndpointCollectibleScanPage = {
image: convertScanToEndpointScanImage(scan, index),
parentPages: convertSourceToEndpointSource({ scans: [collectible] }),
slug,
translations:
collectible.translations?.map(({ language, title, description, pretitle, subtitle }) => ({
@ -66,6 +63,12 @@ export const getBySlugEndpointScanPage: CollectionEndpoint = {
})) ?? [],
...(isDefined(previousIndex) ? { previousIndex } : {}),
...(isDefined(nextIndex) ? { nextIndex } : {}),
backlinks: [
{
type: Collections.Collectibles,
value: convertCollectibleToEndpointCollectiblePreview(collectible),
},
],
};
res.status(200).send(scanPage);

View File

@ -7,8 +7,8 @@ import {
convertCreditsToEndpointCredits,
convertImageToEndpointPayloadImage,
convertScanToEndpointScanImage,
convertSourceToEndpointSource,
} from "../../../utils/endpoints";
import { convertCollectibleToEndpointCollectiblePreview } from "./getBySlugEndpoint";
export const getBySlugEndpointScans = createGetByEndpoint({
collection: Collections.Collectibles,
@ -29,7 +29,12 @@ export const getBySlugEndpointScans = createGetByEndpoint({
})) ?? [],
...(isImage(thumbnail) ? { thumbnail: convertImageToEndpointPayloadImage(thumbnail) } : {}),
...(scansEnabled && scans ? handleScans(scans) : { credits: [], pages: [] }),
parentPages: convertSourceToEndpointSource({ collectibles: [collectible] }),
backlinks: [
{
type: Collections.Collectibles,
value: convertCollectibleToEndpointCollectiblePreview(collectible),
},
],
};
},
});

View File

@ -7,6 +7,7 @@ import {
convertCreditsToEndpointCredits,
convertMediaThumbnailToEndpointPayloadImage,
convertRTCToEndpointRTC,
convertRelationshipsToEndpointRelations,
getLanguageId,
} from "../../../utils/endpoints";
import { Collections } from "../../../shared/payload/constants";
@ -15,6 +16,7 @@ import {
EndpointFilePreview,
EndpointFile,
} from "../../../shared/payload/endpoint-types";
import { findIncomingRelationships } from "payloadcms-relationships";
export const getByID: CollectionEndpoint = {
method: "get",
@ -44,7 +46,7 @@ export const getByID: CollectionEndpoint = {
return res.sendStatus(404);
}
return res.status(200).json(convertFileToEndpointFile(result));
return res.status(200).json(await convertFileToEndpointFile(result));
} catch {
return res.sendStatus(404);
}
@ -79,8 +81,8 @@ export const convertFileToEndpointFilePreview = ({
: {}),
});
const convertFileToEndpointFile = (file: File & PayloadMedia): EndpointFile => {
const { translations, createdAt, updatedAt, filesize, credits } = file;
const convertFileToEndpointFile = async (file: File & PayloadMedia): Promise<EndpointFile> => {
const { translations, createdAt, updatedAt, filesize, credits, id } = file;
return {
...convertFileToEndpointFilePreview(file),
@ -96,5 +98,8 @@ const convertFileToEndpointFile = (file: File & PayloadMedia): EndpointFile => {
...(isNotEmpty(description) ? { description: convertRTCToEndpointRTC(description) } : {}),
})) ?? [],
credits: convertCreditsToEndpointCredits(credits),
backlinks: convertRelationshipsToEndpointRelations(
await findIncomingRelationships(Collections.Files, id)
),
};
};

View File

@ -1,11 +1,8 @@
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
import { iconField } from "../../fields/iconField/iconField";
import { rowField } from "../../fields/rowField/rowField";
import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { Collections, CollectionGroups } from "../../shared/payload/constants";
import { Folder } from "../../types/collections";
import { isPayloadType } from "../../utils/asserts";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { createEditor } from "../../utils/editor";
import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint";
@ -44,16 +41,7 @@ export const Folders = buildCollectionConfig({
},
endpoints: [getBySlugEndpoint],
fields: [
rowField([
slugField({ name: fields.slug }),
iconField({ name: fields.icon }),
backPropagationField({
name: fields.parentFolders,
relationTo: Collections.Folders,
hasMany: true,
where: ({ id }) => ({ "sections.subfolders": { equals: id } }),
}),
]),
rowField([slugField({ name: fields.slug }), iconField({ name: fields.icon })]),
translatedFields({
name: fields.translations,
admin: { useAsTitle: fields.translationsName },
@ -112,21 +100,4 @@ export const Folders = buildCollectionConfig({
hasMany: true,
},
],
custom: {
getBackPropagatedRelationships: ({ files, sections }: Folder) => {
const result: string[] = [];
files?.forEach(({ relationTo, value }) => {
if (relationTo === "collectibles" || relationTo === "pages") {
result.push(isPayloadType(value) ? value.id : value);
}
});
sections?.forEach(({ subfolders }) =>
subfolders?.forEach((folder) => {
result.push(isPayloadType(folder) ? folder.id : folder);
})
);
return result;
},
},
});

View File

@ -1,4 +1,5 @@
import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint";
import { findIncomingRelationships } from "payloadcms-relationships";
import { Collections } from "../../../shared/payload/constants";
import { EndpointFolderPreview, EndpointFolder } from "../../../shared/payload/endpoint-types";
import { Folder, Language } from "../../../types/collections";
@ -12,7 +13,7 @@ import {
isPublished,
isVideo,
} from "../../../utils/asserts";
import { convertSourceToEndpointSource, getLanguageId } from "../../../utils/endpoints";
import { convertRelationshipsToEndpointRelations, getLanguageId } from "../../../utils/endpoints";
import { convertAudioToEndpointAudioPreview } from "../../Audios/endpoints/getByID";
import { convertCollectibleToEndpointCollectiblePreview } from "../../Collectibles/endpoints/getBySlugEndpoint";
import { convertFileToEndpointFilePreview } from "../../Files/endpoints/getByID";
@ -43,8 +44,8 @@ export const convertFolderToEndpointFolderPreview = ({
})) ?? [],
});
const convertFolderToEndpointFolder = (folder: Folder): EndpointFolder => {
const { translations, sections, files, parentFolders } = folder;
const convertFolderToEndpointFolder = async (folder: Folder): Promise<EndpointFolder> => {
const { translations, sections, files, id } = folder;
return {
...convertFolderToEndpointFolderPreview(folder),
@ -116,7 +117,9 @@ const convertFolderToEndpointFolder = (folder: Folder): EndpointFolder => {
return [];
}
}) ?? [],
parentPages: convertSourceToEndpointSource({ folders: parentFolders }),
backlinks: convertRelationshipsToEndpointRelations(
await findIncomingRelationships(Collections.Folders, id)
),
};
};

View File

@ -6,6 +6,7 @@ import {
convertAttributesToEndpointAttributes,
convertCreditsToEndpointCredits,
convertRTCToEndpointRTC,
convertRelationshipsToEndpointRelations,
convertSizesToPayloadImages,
getLanguageId,
} from "../../../utils/endpoints";
@ -15,6 +16,7 @@ import {
EndpointImagePreview,
EndpointImage,
} from "../../../shared/payload/endpoint-types";
import { findIncomingRelationships } from "payloadcms-relationships";
export const getByID: CollectionEndpoint = {
method: "get",
@ -44,7 +46,7 @@ export const getByID: CollectionEndpoint = {
return res.sendStatus(404);
}
return res.status(200).json(convertImageToEndpointImage(result));
return res.status(200).json(await convertImageToEndpointImage(result));
} catch {
return res.sendStatus(404);
}
@ -93,8 +95,10 @@ export const convertImageToEndpointImagePreview = ({
...(isPayloadImage(sizes?.og) ? { openGraph: sizes.og } : {}),
});
export const convertImageToEndpointImage = (image: Image & PayloadImage): EndpointImage => {
const { translations, createdAt, updatedAt, filesize, credits } = image;
export const convertImageToEndpointImage = async (
image: Image & PayloadImage
): Promise<EndpointImage> => {
const { translations, createdAt, updatedAt, filesize, credits, id } = image;
return {
...convertImageToEndpointImagePreview(image),
createdAt,
@ -109,5 +113,8 @@ export const convertImageToEndpointImage = (image: Image & PayloadImage): Endpoi
...(isNotEmpty(description) ? { description: convertRTCToEndpointRTC(description) } : {}),
})) ?? [],
credits: convertCreditsToEndpointCredits(credits),
backlinks: convertRelationshipsToEndpointRelations(
await findIncomingRelationships(Collections.Images, id)
),
};
};

View File

@ -1,10 +1,8 @@
import { Where } from "payload/types";
import { breakBlock } from "../../blocks/breakBlock";
import { sectionBlock } from "../../blocks/sectionBlock";
import { transcriptBlock } from "../../blocks/transcriptBlock";
import { QuickFilters, publishStatusFilters } from "../../components/QuickFilters";
import { attributesField } from "../../fields/attributesField/attributesField";
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
import { creditsField } from "../../fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField";
@ -125,29 +123,5 @@ export const Pages = buildVersionedCollectionConfig({
creditsField({ name: fields.credits }),
],
}),
rowField([
backPropagationField({
name: fields.folders,
relationTo: Collections.Folders,
hasMany: true,
where: ({ id }) => ({
and: [
{ "files.value": { equals: id } },
{ "files.relationTo": { equals: Collections.Pages } },
] as Where[],
}),
}),
backPropagationField({
name: fields.collectibles,
hasMany: true,
relationTo: Collections.Collectibles,
where: ({ id }) => ({
and: [
{ "contents.content.value": { equals: id } },
{ "contents.content.relationTo": { equals: Collections.Pages } },
] as Where[],
}),
}),
]),
],
});

View File

@ -1,4 +1,5 @@
import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint";
import { findIncomingRelationships } from "payloadcms-relationships";
import { Collections, BreakBlockType } from "../../../shared/payload/constants";
import {
EndpointPagePreview,
@ -18,7 +19,7 @@ import {
convertCreditsToEndpointCredits,
convertImageToEndpointPayloadImage,
convertRTCToEndpointRTC,
convertSourceToEndpointSource,
convertRelationshipsToEndpointRelations,
} from "../../../utils/endpoints";
import { convertRecorderToEndpointRecorderPreview } from "../../Recorders/endpoints/getByID";
@ -49,8 +50,8 @@ export const convertPageToEndpointPagePreview = ({
updatedAt,
});
const convertPageToEndpointPage = (page: Page): EndpointPage => {
const { translations, collectibles, folders, backgroundImage, createdAt, updatedBy } = page;
const convertPageToEndpointPage = async (page: Page): Promise<EndpointPage> => {
const { translations, backgroundImage, createdAt, updatedBy, id } = page;
return {
...convertPageToEndpointPagePreview(page),
@ -74,7 +75,9 @@ const convertPageToEndpointPage = (page: Page): EndpointPage => {
...(isPayloadType(updatedBy)
? { updatedBy: convertRecorderToEndpointRecorderPreview(updatedBy) }
: {}),
parentPages: convertSourceToEndpointSource({ collectibles, folders }),
backlinks: convertRelationshipsToEndpointRelations(
await findIncomingRelationships(Collections.Pages, id)
),
};
};

View File

@ -5,8 +5,6 @@ import { imageField } from "../../fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { Collections, CollectionGroups } from "../../shared/payload/constants";
import { Video } from "../../types/collections";
import { isPayloadType } from "../../utils/asserts";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { createEditor } from "../../utils/editor";
import { getByID } from "./endpoints/getByID";
@ -130,12 +128,4 @@ export const Videos = buildCollectionConfig({
],
}),
],
custom: {
getBackPropagatedRelationships: ({ platform, platformEnabled }: Video) => {
if (!platform || !platformEnabled) {
return [];
}
return [isPayloadType(platform.channel) ? platform.channel.id : platform.channel];
},
},
});

View File

@ -15,6 +15,7 @@ import {
convertCreditsToEndpointCredits,
convertMediaThumbnailToEndpointPayloadImage,
convertRTCToEndpointRTC,
convertRelationshipsToEndpointRelations,
getLanguageId,
} from "../../../utils/endpoints";
import { Collections } from "../../../shared/payload/constants";
@ -23,6 +24,7 @@ import {
EndpointVideoPreview,
EndpointVideo,
} from "../../../shared/payload/endpoint-types";
import { findIncomingRelationships } from "payloadcms-relationships";
export const getByID: CollectionEndpoint = {
method: "get",
@ -52,7 +54,7 @@ export const getByID: CollectionEndpoint = {
return res.sendStatus(404);
}
return res.status(200).json(convertVideoToEndpointVideo(result));
return res.status(200).json(await convertVideoToEndpointVideo(result));
} catch {
return res.sendStatus(404);
}
@ -98,8 +100,8 @@ export const convertVideoToEndpointVideoPreview = ({
}) ?? [],
});
const convertVideoToEndpointVideo = (video: Video & PayloadMedia): EndpointVideo => {
const { translations, createdAt, updatedAt, filesize, platform, platformEnabled, credits } =
const convertVideoToEndpointVideo = async (video: Video & PayloadMedia): Promise<EndpointVideo> => {
const { translations, createdAt, updatedAt, filesize, platform, platformEnabled, credits, id } =
video;
return {
@ -125,5 +127,8 @@ const convertVideoToEndpointVideo = (video: Video & PayloadMedia): EndpointVideo
}
: {}),
credits: convertCreditsToEndpointCredits(credits),
backlinks: convertRelationshipsToEndpointRelations(
await findIncomingRelationships(Collections.Videos, id)
),
};
};

View File

@ -1,5 +1,4 @@
import { CollectionConfig } from "payload/types";
import { backPropagationField } from "../../fields/backPropagationField/backPropagationField";
import { rowField } from "../../fields/rowField/rowField";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { Collections, CollectionGroups } from "../../shared/payload/constants";
@ -31,11 +30,5 @@ export const VideosChannels: CollectionConfig = buildCollectionConfig({
{ name: fields.title, type: "text", required: true },
{ name: fields.subscribers, type: "number", required: true },
]),
backPropagationField({
name: fields.videos,
relationTo: Collections.Videos,
hasMany: true,
where: ({ id }) => ({ "platform.channel": { equals: id } }),
}),
],
});

View File

@ -2,7 +2,6 @@ import { GlobalConfig } from "payload/types";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { imageField } from "../../fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField";
import { globalAfterChangeWebhook } from "../../hooks/afterOperationWebhook";
import { getConfigEndpoint } from "./endpoints/getConfigEndpoint";
import { Collections, CollectionGroups } from "../../shared/payload/constants";
@ -31,9 +30,6 @@ export const WebsiteConfig: GlobalConfig = {
},
access: { update: mustBeAdmin, read: mustBeAdmin },
endpoints: [getConfigEndpoint],
hooks: {
afterChange: [globalAfterChangeWebhook],
},
fields: [
rowField([
{

View File

@ -124,7 +124,7 @@ export const getAllSDKUrlsEndpoint: Endpoint = {
},
};
export const getSDKUrlsForDocument = (collection: Collections, doc: any): string[] => {
const getSDKUrlsForDocument = (collection: Collections, doc: any): string[] => {
switch (collection) {
case Collections.WebsiteConfig:
return [getSDKEndpoint.getConfigEndpoint()];

View File

@ -1,51 +0,0 @@
import payload, { GeneratedTypes } from "payload";
import { FieldBase, SingleRelationshipField } from "payload/dist/fields/config/types";
import { Where } from "payload/types";
import { isEmpty } from "../../utils/asserts";
type BackPropagationField = FieldBase & {
where: (data: any) => Where;
relationTo: keyof GeneratedTypes["collections"];
hasMany?: boolean;
};
export const backPropagationField = ({
admin,
hooks: { beforeChange = [], afterRead = [], ...otherHooks } = {},
where,
hasMany = false,
...params
}: BackPropagationField): SingleRelationshipField => ({
...params,
type: "relationship",
hasMany: hasMany,
admin: { ...admin, readOnly: true },
hooks: {
...otherHooks,
beforeChange: [
...beforeChange,
({ siblingData }) => {
delete siblingData[params.name];
},
],
afterRead: [
...afterRead,
async ({ data, context }) => {
if (isEmpty(data?.id) || context.stopPropagation) {
return hasMany ? [] : undefined;
}
const result = await payload.find({
collection: params.relationTo,
where: where(data),
limit: 100,
depth: 0,
context: { stopPropagation: true },
});
if (hasMany) {
return result.docs.map((doc) => doc.id);
} else {
return result.docs[0]?.id;
}
},
],
},
});

View File

@ -1,47 +0,0 @@
import payload, { GeneratedTypes } from "payload";
import { SanitizedCollectionConfig, SanitizedGlobalConfig } from "payload/types";
export const getAddedBackPropagationRelationships = async (
config: SanitizedCollectionConfig | SanitizedGlobalConfig,
doc: any,
previousDoc?: any
): Promise<string[]> => {
if (!("getBackPropagatedRelationships" in config.custom)) {
return [];
}
const getBackPropagatedRelationships: (doc: any) => string[] =
config.custom.getBackPropagatedRelationships;
if (!previousDoc) {
return getBackPropagatedRelationships(doc);
}
let currentIds: string[];
let previousIds: string[];
if (config.versions.drafts) {
const versions = await payload.findVersions({
collection: config.slug as keyof GeneratedTypes["collections"],
sort: "-updatedAt",
limit: 2,
where: {
and: [{ parent: { equals: doc.id } }, { "version._status": { equals: "published" } }],
},
});
const currentVersion = versions.docs[0]?.version;
const previousVersion = versions.docs[1]?.version;
if (!currentVersion) return [];
if (!previousVersion) return getBackPropagatedRelationships(currentVersion);
currentIds = getBackPropagatedRelationships(currentVersion);
previousIds = getBackPropagatedRelationships(previousVersion);
} else {
currentIds = getBackPropagatedRelationships(doc);
previousIds = getBackPropagatedRelationships(previousDoc);
}
return currentIds.filter((id) => !previousIds.includes(id));
};

View File

@ -1,99 +0,0 @@
import {
AfterDeleteHook,
AfterChangeHook as CollectionAfterChangeHook,
} from "payload/dist/collections/config/types";
import { AfterChangeHook as GlobalAfterChangeHook } from "payload/dist/globals/config/types";
import { getSDKUrlsForDocument } from "../endpoints/getAllSDKUrlsEndpoint";
import { getAddedBackPropagationRelationships } from "../fields/backPropagationField/backPropagationUtils";
import { Collections } from "../shared/payload/constants";
import { AfterOperationWebHookMessage } from "../shared/payload/webhooks";
export const globalAfterChangeWebhook: GlobalAfterChangeHook = async ({
global,
doc,
previousDoc,
}) => {
const collection = global.slug as Collections;
await sendWebhookMessage({
collection,
addedDependantIds: await getAddedBackPropagationRelationships(global, doc, previousDoc),
urls: getSDKUrlsForDocument(collection, doc),
});
return doc;
};
export const collectionAfterChangeWebhook: CollectionAfterChangeHook = async ({
collection,
doc,
previousDoc,
operation,
}) => {
const collectionSlug = collection.slug as Collections;
console.log("afterChange", operation, collectionSlug, doc.id);
if ("_status" in doc && doc._status === "draft") {
return doc;
}
if (!("id" in doc)) {
return doc;
}
await sendWebhookMessage({
collection: collectionSlug,
id: doc.id,
addedDependantIds: await getAddedBackPropagationRelationships(collection, doc, previousDoc),
urls: getSDKUrlsForDocument(collectionSlug, doc),
});
return doc;
};
export const afterDeleteWebhook: AfterDeleteHook = async ({ collection, doc }) => {
const collectionSlug = collection.slug as Collections;
console.log("afterDelete", collection.slug, doc.id);
if (!("id" in doc)) {
return doc;
}
await sendWebhookMessage({
collection: collectionSlug,
id: doc.id,
addedDependantIds: [],
urls: getSDKUrlsForDocument(collectionSlug, doc),
});
return doc;
};
const webhookTargets: { url: string; token: string }[] = [
{
url: process.env.WEB_SERVER_HOOK_URL ?? "",
token: process.env.WEB_SERVER_HOOK_TOKEN ?? "",
},
{
url: process.env.MEILISEARCH_HOOK_URL ?? "",
token: process.env.MEILISEARCH_HOOK_TOKEN ?? "",
},
];
const sendWebhookMessage = async (message: AfterOperationWebHookMessage) => {
try {
await Promise.all(
webhookTargets.flatMap(({ url, token }) => {
if (!url) return;
return fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(message),
method: "POST",
});
})
);
} catch (e) {
console.warn("Error while sending webhook", e);
}
};

View File

@ -31,6 +31,9 @@ import { getAllIds } from "./endpoints/getAllIdsEndpoint";
import { getAllSDKUrlsEndpoint } from "./endpoints/getAllSDKUrlsEndpoint";
import { createEditor } from "./utils/editor";
import { Collections } from "./shared/payload/constants";
import { relationshipsPlugin } from "payloadcms-relationships";
import { shownOnlyToAdmin } from "./accesses/collections/shownOnlyToAdmin";
import { mustBeAdmin } from "./accesses/fields/mustBeAdmin";
const configuredSftpAdapter = sftpAdapter({
connectOptions: {
@ -96,6 +99,19 @@ export default buildConfig({
skip: () => true,
},
plugins: [
relationshipsPlugin({
collectionConfig: {
admin: {
hidden: shownOnlyToAdmin,
},
access: {
update: mustBeAdmin,
create: mustBeAdmin,
delete: mustBeAdmin,
},
},
}),
cloudStorage({
collections: {
[Collections.Videos]: {

View File

@ -2,7 +2,7 @@ import "dotenv/config";
import express from "express";
import path from "path";
import payload from "payload";
import { isDefined, isUndefined } from "./utils/asserts";
import { isUndefined } from "./utils/asserts";
import { Collections, RecordersRoles } from "./shared/payload/constants";
const app = express();
@ -28,29 +28,30 @@ const start = async () => {
express: app,
onInit: async () => {
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
const recorders = await payload.find({ collection: Collections.Recorders });
// If no recorders, we seed some initial data
if (
isDefined(process.env.SEEDING_ADMIN_EMAIL) &&
isDefined(process.env.SEEDING_ADMIN_PASSWORD) &&
isDefined(process.env.SEEDING_ADMIN_USERNAME)
) {
if (recorders.docs.length === 0) {
payload.logger.info("Seeding some initial data");
const seedFirstUser = async () => {
const recorders = await payload.find({ collection: Collections.Recorders });
await payload.create({
collection: Collections.Recorders,
data: {
email: process.env.SEEDING_ADMIN_EMAIL,
password: process.env.SEEDING_ADMIN_PASSWORD,
username: process.env.SEEDING_ADMIN_USERNAME,
role: [RecordersRoles.Admin, RecordersRoles.Api],
anonymize: false,
},
});
}
}
if (recorders.docs.length > 0) return;
if (isUndefined(process.env.SEEDING_ADMIN_EMAIL)) return;
if (isUndefined(process.env.SEEDING_ADMIN_PASSWORD)) return;
if (isUndefined(process.env.SEEDING_ADMIN_USERNAME)) return;
payload.logger.info("Seeding your first user");
await payload.create({
collection: Collections.Recorders,
data: {
email: process.env.SEEDING_ADMIN_EMAIL,
password: process.env.SEEDING_ADMIN_PASSWORD,
username: process.env.SEEDING_ADMIN_USERNAME,
role: [RecordersRoles.Admin, RecordersRoles.Api],
anonymize: false,
},
});
};
await seedFirstUser();
},
});

@ -1 +1 @@
Subproject commit 824f7085f9e0787f97ef9afd11cb5320a2cd6208
Subproject commit 7d6f5ffb704f4ecddfb0e0982ce9eb79d39d450d

View File

@ -40,6 +40,7 @@ export interface Config {
currencies: Currency;
wordings: Wording;
"generic-contents": GenericContent;
relationships: Relationship;
"payload-preferences": PayloadPreference;
"payload-migrations": PayloadMigration;
};
@ -96,8 +97,6 @@ export interface Page {
credits?: Credits;
id?: string | null;
}[];
folders?: (string | Folder)[] | null;
collectibles?: (string | Collectible)[] | null;
updatedBy: string | Recorder;
updatedAt: string;
createdAt: string;
@ -352,79 +351,6 @@ export interface Recorder {
lockUntil?: string | null;
password?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "folders".
*/
export interface Folder {
id: string;
slug: string;
icon?: string | null;
parentFolders?: (string | Folder)[] | null;
translations: {
language: string | Language;
name: string;
description?: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ("ltr" | "rtl") | null;
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
id?: string | null;
}[];
sections?:
| {
translations?:
| {
language: string | Language;
name: string;
id?: string | null;
}[]
| null;
subfolders?: (string | Folder)[] | null;
id?: string | null;
}[]
| null;
files?:
| (
| {
relationTo: "collectibles";
value: string | Collectible;
}
| {
relationTo: "pages";
value: string | Page;
}
| {
relationTo: "videos";
value: string | Video;
}
| {
relationTo: "images";
value: string | Image;
}
| {
relationTo: "audios";
value: string | Audio;
}
| {
relationTo: "files";
value: string | File;
}
)[]
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "collectibles".
@ -608,8 +534,6 @@ export interface Collectible {
id?: string | null;
}[]
| null;
folders?: (string | Folder)[] | null;
parentItems?: (string | Collectible)[] | null;
updatedBy: string | Recorder;
updatedAt: string;
createdAt: string;
@ -951,7 +875,78 @@ export interface VideosChannel {
url: string;
title: string;
subscribers: number;
videos?: (string | Video)[] | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "folders".
*/
export interface Folder {
id: string;
slug: string;
icon?: string | null;
translations: {
language: string | Language;
name: string;
description?: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ("ltr" | "rtl") | null;
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
id?: string | null;
}[];
sections?:
| {
translations?:
| {
language: string | Language;
name: string;
id?: string | null;
}[]
| null;
subfolders?: (string | Folder)[] | null;
id?: string | null;
}[]
| null;
files?:
| (
| {
relationTo: "collectibles";
value: string | Collectible;
}
| {
relationTo: "pages";
value: string | Page;
}
| {
relationTo: "videos";
value: string | Video;
}
| {
relationTo: "images";
value: string | Image;
}
| {
relationTo: "audios";
value: string | Audio;
}
| {
relationTo: "files";
value: string | File;
}
)[]
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
@ -1082,6 +1077,260 @@ export interface Wording {
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationships".
*/
export interface Relationship {
id: string;
document:
| {
relationTo: "pages";
value: string | Page;
}
| {
relationTo: "collectibles";
value: string | Collectible;
}
| {
relationTo: "folders";
value: string | Folder;
}
| {
relationTo: "chronology-events";
value: string | ChronologyEvent;
}
| {
relationTo: "images";
value: string | Image;
}
| {
relationTo: "audios";
value: string | Audio;
}
| {
relationTo: "media-thumbnails";
value: string | MediaThumbnail;
}
| {
relationTo: "videos";
value: string | Video;
}
| {
relationTo: "videos-subtitles";
value: string | VideoSubtitle;
}
| {
relationTo: "videos-channels";
value: string | VideosChannel;
}
| {
relationTo: "files";
value: string | File;
}
| {
relationTo: "scans";
value: string | Scan;
}
| {
relationTo: "tags";
value: string | Tag;
}
| {
relationTo: "attributes";
value: string | Attribute;
}
| {
relationTo: "credits-roles";
value: string | CreditsRole;
}
| {
relationTo: "recorders";
value: string | Recorder;
}
| {
relationTo: "languages";
value: string | Language;
}
| {
relationTo: "currencies";
value: string | Currency;
}
| {
relationTo: "wordings";
value: string | Wording;
}
| {
relationTo: "generic-contents";
value: string | GenericContent;
};
incomingRelations?:
| (
| {
relationTo: "pages";
value: string | Page;
}
| {
relationTo: "collectibles";
value: string | Collectible;
}
| {
relationTo: "folders";
value: string | Folder;
}
| {
relationTo: "chronology-events";
value: string | ChronologyEvent;
}
| {
relationTo: "images";
value: string | Image;
}
| {
relationTo: "audios";
value: string | Audio;
}
| {
relationTo: "media-thumbnails";
value: string | MediaThumbnail;
}
| {
relationTo: "videos";
value: string | Video;
}
| {
relationTo: "videos-subtitles";
value: string | VideoSubtitle;
}
| {
relationTo: "videos-channels";
value: string | VideosChannel;
}
| {
relationTo: "files";
value: string | File;
}
| {
relationTo: "scans";
value: string | Scan;
}
| {
relationTo: "tags";
value: string | Tag;
}
| {
relationTo: "attributes";
value: string | Attribute;
}
| {
relationTo: "credits-roles";
value: string | CreditsRole;
}
| {
relationTo: "recorders";
value: string | Recorder;
}
| {
relationTo: "languages";
value: string | Language;
}
| {
relationTo: "currencies";
value: string | Currency;
}
| {
relationTo: "wordings";
value: string | Wording;
}
| {
relationTo: "generic-contents";
value: string | GenericContent;
}
)[]
| null;
outgoingRelations: (
| {
relationTo: "pages";
value: string | Page;
}
| {
relationTo: "collectibles";
value: string | Collectible;
}
| {
relationTo: "folders";
value: string | Folder;
}
| {
relationTo: "chronology-events";
value: string | ChronologyEvent;
}
| {
relationTo: "images";
value: string | Image;
}
| {
relationTo: "audios";
value: string | Audio;
}
| {
relationTo: "media-thumbnails";
value: string | MediaThumbnail;
}
| {
relationTo: "videos";
value: string | Video;
}
| {
relationTo: "videos-subtitles";
value: string | VideoSubtitle;
}
| {
relationTo: "videos-channels";
value: string | VideosChannel;
}
| {
relationTo: "files";
value: string | File;
}
| {
relationTo: "scans";
value: string | Scan;
}
| {
relationTo: "tags";
value: string | Tag;
}
| {
relationTo: "attributes";
value: string | Attribute;
}
| {
relationTo: "credits-roles";
value: string | CreditsRole;
}
| {
relationTo: "recorders";
value: string | Recorder;
}
| {
relationTo: "languages";
value: string | Language;
}
| {
relationTo: "currencies";
value: string | Currency;
}
| {
relationTo: "wordings";
value: string | Wording;
}
| {
relationTo: "generic-contents";
value: string | GenericContent;
}
)[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".

View File

@ -1,6 +1,5 @@
import { GeneratedTypes } from "payload";
import { CollectionConfig } from "payload/types";
import { afterDeleteWebhook, collectionAfterChangeWebhook } from "../hooks/afterOperationWebhook";
import { formatToPascalCase } from "./string";
type CollectionConfigWithPlugins = CollectionConfig;
@ -11,18 +10,9 @@ export type BuildCollectionConfig = Omit<
> & {
slug: keyof GeneratedTypes["collections"];
labels: { singular: string; plural: string };
custom?: {
getBackPropagatedRelationships?: (object: any) => string[];
[key: string]: unknown;
};
};
export const buildCollectionConfig = (config: BuildCollectionConfig): CollectionConfig => ({
...config,
typescript: { interface: formatToPascalCase(config.labels.singular) },
hooks: {
...config.hooks,
afterChange: [...(config.hooks?.afterChange ?? []), collectionAfterChangeWebhook],
afterDelete: [...(config.hooks?.afterDelete ?? []), afterDeleteWebhook],
},
});

View File

@ -1,18 +1,22 @@
import { convertAudioToEndpointAudioPreview } from "../collections/Audios/endpoints/getByID";
import { convertEventToEndpointEvent } from "../collections/ChronologyEvents/endpoints/getAllEndpoint";
import { convertCollectibleToEndpointCollectiblePreview } from "../collections/Collectibles/endpoints/getBySlugEndpoint";
import { convertFileToEndpointFilePreview } from "../collections/Files/endpoints/getByID";
import { convertFolderToEndpointFolderPreview } from "../collections/Folders/endpoints/getBySlugEndpoint";
import { convertImageToEndpointImagePreview } from "../collections/Images/endpoints/getByID";
import { convertPageToEndpointPagePreview } from "../collections/Pages/endpoints/getBySlugEndpoint";
import { convertRecorderToEndpointRecorderPreview } from "../collections/Recorders/endpoints/getByID";
import { convertVideoToEndpointVideoPreview } from "../collections/Videos/endpoints/getByID";
import { AttributeTypes } from "../shared/payload/constants";
import { AttributeTypes, Collections } from "../shared/payload/constants";
import {
EndpointTag,
EndpointSource,
EndpointSourcePreview,
EndpointRole,
EndpointCredit,
EndpointAttribute,
PayloadImage,
EndpointScanImage,
EndpointPayloadImage,
EndpointRelation,
} from "../shared/payload/endpoint-types";
import {
RichTextContent,
@ -29,14 +33,13 @@ import {
} from "../shared/payload/rich-text";
import {
Audio,
Collectible,
Credits,
CreditsRole,
Folder,
Image,
Language,
MediaThumbnail,
NumberBlock,
Relationship,
Scan,
Tag,
TagsBlock,
@ -47,12 +50,11 @@ import {
isAudio,
isDefined,
isEmpty,
isFile,
isImage,
isNotEmpty,
isPayloadArrayType,
isPayloadImage,
isPayloadType,
isPublished,
isVideo,
} from "./asserts";
@ -139,86 +141,64 @@ export const convertRTCToEndpointRTC = (
};
};
// TODO: Handle URL sources
export const convertSourceToEndpointSource = ({
collectibles,
folders,
gallery,
scans,
}: {
collectibles?: (string | Collectible)[] | null | undefined;
scans?: (string | Collectible)[] | null | undefined;
gallery?: (string | Collectible)[] | null | undefined;
folders?: (string | Folder)[] | null | undefined;
}): EndpointSource[] => {
const result: EndpointSource[] = [];
export const convertRelationshipsToEndpointRelations = (
relationships: Relationship["incomingRelations"] | Relationship["outgoingRelations"]
): EndpointRelation[] =>
relationships?.flatMap<EndpointRelation>(({ relationTo, value }) => {
if (!isPayloadType(value)) return [];
switch (relationTo) {
case Collections.Folders:
return { type: Collections.Folders, value: convertFolderToEndpointFolderPreview(value) };
const convertFolderToEndpointSourcePreview = ({
id,
slug,
translations,
}: Folder): EndpointSourcePreview => ({
id,
slug,
translations: translations.map(({ language, name }) => ({
language: isPayloadType(language) ? language.id : language,
title: name,
})),
});
case Collections.Pages:
return { type: Collections.Pages, value: convertPageToEndpointPagePreview(value) };
const convertCollectibleToEndpointSourcePreview = ({
id,
slug,
translations,
}: Collectible): EndpointSourcePreview => ({
id,
slug,
translations: translations.map(({ language, title, pretitle, subtitle }) => ({
language: isPayloadType(language) ? language.id : language,
title,
...(isNotEmpty(pretitle) ? { pretitle } : {}),
...(isNotEmpty(subtitle) ? { subtitle } : {}),
})),
});
case Collections.Collectibles:
return {
type: Collections.Collectibles,
value: convertCollectibleToEndpointCollectiblePreview(value),
};
if (collectibles && isPayloadArrayType(collectibles)) {
collectibles.filter(isPublished).forEach((collectible) => {
result.push({
type: "collectible",
collectible: convertCollectibleToEndpointSourcePreview(collectible),
});
});
}
case Collections.Images:
if (!isImage(value)) return [];
return { type: Collections.Images, value: convertImageToEndpointImagePreview(value) };
if (scans && isPayloadArrayType(scans)) {
scans.filter(isPublished).forEach((collectible) => {
result.push({
type: "scans",
collectible: convertCollectibleToEndpointSourcePreview(collectible),
});
});
}
case Collections.Videos:
if (!isVideo(value)) return [];
return { type: Collections.Videos, value: convertVideoToEndpointVideoPreview(value) };
if (gallery && isPayloadArrayType(gallery)) {
gallery.filter(isPublished).forEach((collectible) => {
result.push({
type: "gallery",
collectible: convertCollectibleToEndpointSourcePreview(collectible),
});
});
}
case Collections.Audios:
if (!isAudio(value)) return [];
return { type: Collections.Audios, value: convertAudioToEndpointAudioPreview(value) };
if (folders && isPayloadArrayType(folders)) {
folders.forEach((folder) => {
result.push({
type: "folder",
folder: convertFolderToEndpointSourcePreview(folder),
});
});
}
case Collections.Files:
if (!isFile(value)) return [];
return { type: Collections.Files, value: convertFileToEndpointFilePreview(value) };
return result;
};
case Collections.Recorders:
return {
type: Collections.Recorders,
value: convertRecorderToEndpointRecorderPreview(value),
};
case Collections.ChronologyEvents:
return { type: Collections.ChronologyEvents, value: convertEventToEndpointEvent(value) };
case Collections.MediaThumbnails:
case Collections.VideosSubtitles:
case Collections.VideosChannels:
case Collections.Scans:
case Collections.Tags:
case Collections.Attributes:
case Collections.CreditsRole:
case Collections.Languages:
case Collections.Currencies:
case Collections.Wordings:
case Collections.GenericContents:
default:
return [];
}
}) ?? [];
export const getDomainFromUrl = (url: string): string => {
const urlObject = new URL(url);