Compare commits

..

1 Commits

Author SHA1 Message Date
DrMint 6c021fdc94 Try migrating to absolute path but payload sucks 2024-07-13 18:18:03 +02:00
100 changed files with 1538 additions and 2079 deletions

View File

@ -13,11 +13,8 @@ SEEDING_ADMIN_USERNAME=admin_name
SEEDING_ADMIN_EMAIL=email@domain.com SEEDING_ADMIN_EMAIL=email@domain.com
SEEDING_ADMIN_PASSWORD=somepassword SEEDING_ADMIN_PASSWORD=somepassword
WEB_SERVER_HOOK_TOKEN=webhooktoken5e6ea45ef4e66eaa151612bdcb599df WEB_HOOK_TOKEN=webhooktoken5e6ea45ef4e66eaa151612bdcb599df
WEB_SERVER_HOOK_URL=https://accords-library.com/some/path WEB_HOOK_URI=https://accords-library.com/some/path
MEILISEARCH_HOOK_TOKEN=webhooktoken5e6ea45ef4e66eaa151612bdcb599df
MEILISEARCH_HOOK_URL=https://meili.domain.com
SFTP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nxxxxxxxxxx..." SFTP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nxxxxxxxxxx..."
SFTP_USERNAME=someuser SFTP_USERNAME=someuser

View File

@ -1,2 +0,0 @@
src/shared/*
dist

View File

@ -1,7 +1,7 @@
{ {
"editor.rulers": [100], "editor.rulers": [100],
"editor.tabSize": 2, "editor.tabSize": 2,
"typescript.preferences.importModuleSpecifier": "relative", "typescript.preferences.importModuleSpecifier": "non-relative",
"explorer.fileNesting.enabled": true, "explorer.fileNesting.enabled": true,
"explorer.fileNesting.patterns": { "explorer.fileNesting.patterns": {
"package.json": ".git*, package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, .ncurc.*, .nvmrc, *.config.cjs, *.config.js, *.config.ts, *config.json, .*ignore", "package.json": ".git*, package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb, .ncurc.*, .nvmrc, *.config.cjs, *.config.js, *.config.ts, *config.json, .*ignore",

806
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
"build:server": "tsc", "build:server": "tsc",
"build": "npm run copyfiles && npm run build:payload && npm run build:server", "build": "npm run copyfiles && npm run build:payload && npm run build:server",
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js", "serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/", "copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/ && copyfiles -u 1 \"src/sdk.ts\" dist/ && copyfiles -u 1 \"src/constants.ts\" dist/ && copyfiles -u 1 \"src/types/collections.ts\" dist/",
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types", "generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types",
"unused-exports": "ts-unused-exports ./tsconfig.json --excludePathsFromReport='src/payload.config.ts;src/types/collections.ts;src/shared/*'", "unused-exports": "ts-unused-exports ./tsconfig.json --excludePathsFromReport='src/payload.config.ts;src/types/collections.ts;src/shared/*'",
"prettier": "prettier --list-different --end-of-line auto --write src", "prettier": "prettier --list-different --end-of-line auto --write src",
@ -18,23 +18,22 @@
"fetch-submodules": "cd src/shared && git pull && cd ../..", "fetch-submodules": "cd src/shared && git pull && cd ../..",
"precommit": "npm run fetch-submodules && npm run generate:types && npm run prettier && npm run unused-exports && npm run tsc", "precommit": "npm run fetch-submodules && npm run generate:types && npm run prettier && npm run unused-exports && npm run tsc",
"upgrade": "ncu", "upgrade": "ncu",
"prod": "rm -rf build && rm -rf dist && npm ci && npm run build && npm run serve" "prod": "rm -rf build && rm -rf dist && npm ci && npm run precommit && npm run build && npm run serve"
}, },
"dependencies": { "dependencies": {
"@fontsource/vollkorn": "5.0.20", "@fontsource/vollkorn": "5.0.20",
"@iconify-json/material-symbols": "^1.1.87", "@iconify-json/material-symbols": "^1.1.83",
"@payloadcms/bundler-webpack": "1.0.7", "@payloadcms/bundler-webpack": "1.0.7",
"@payloadcms/db-mongodb": "1.7.2", "@payloadcms/db-mongodb": "1.5.2",
"@payloadcms/richtext-lexical": "0.11.3", "@payloadcms/richtext-lexical": "0.11.2",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"language-tags": "1.0.9", "language-tags": "1.0.9",
"luxon": "3.5.0", "luxon": "3.4.4",
"payload": "2.26.0", "payload": "2.23.1",
"payloadcms-relationships": "github:DrMint/payloadcms-relationships",
"payloadcms-sftp-storage": "1.0.1", "payloadcms-sftp-storage": "1.0.1",
"sharp": "0.33.4", "sharp": "0.33.4",
"styled-components": "6.1.12" "styled-components": "6.1.11"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "4.17.21", "@types/express": "4.17.21",
@ -45,9 +44,11 @@
"@types/styled-components": "5.1.34", "@types/styled-components": "5.1.34",
"copyfiles": "2.4.1", "copyfiles": "2.4.1",
"nodemon": "3.1.4", "nodemon": "3.1.4",
"prettier": "3.3.3", "prettier": "3.3.2",
"ts-node": "10.9.2", "ts-node": "10.9.2",
"ts-unused-exports": "10.1.0", "ts-unused-exports": "10.1.0",
"tsconfig-paths": "^4.2.0",
"tsconfig-paths-webpack-plugin": "^4.1.0",
"typescript": "5.4.5" "typescript": "5.4.5"
} }
} }

View File

@ -1,7 +1,7 @@
import { Access } from "payload/config"; import { Access } from "payload/config";
import { Recorder } from "../../types/collections"; import { RecordersRoles } from "src/shared/payload/constants";
import { isDefined, isUndefined } from "../../utils/asserts"; import { Recorder } from "src/types/collections";
import { RecordersRoles } from "../../shared/payload/constants"; import { isUndefined, isDefined } from "src/utils/asserts";
export const mustBeAdmin: Access<unknown, Recorder> = ({ req: { user } }): boolean => { export const mustBeAdmin: Access<unknown, Recorder> = ({ req: { user } }): boolean => {
if (isUndefined(user)) return false; if (isUndefined(user)) return false;

View File

@ -1,7 +1,7 @@
import { Access } from "payload/config"; import { Access } from "payload/config";
import { Recorder } from "../../types/collections"; import { RecordersRoles } from "src/shared/payload/constants";
import { isUndefined } from "../../utils/asserts"; import { Recorder } from "src/types/collections";
import { RecordersRoles } from "../../shared/payload/constants"; import { isUndefined } from "src/utils/asserts";
export const mustBeAdminOrSelf: Access<unknown, Recorder> = ({ req: { user } }) => { export const mustBeAdminOrSelf: Access<unknown, Recorder> = ({ req: { user } }) => {
if (isUndefined(user)) return false; if (isUndefined(user)) return false;

View File

@ -1,7 +1,7 @@
import { User } from "payload/auth"; import { User } from "payload/auth";
import { Recorder } from "../../types/collections"; import { RecordersRoles } from "src/shared/payload/constants";
import { isDefined, isUndefined } from "../../utils/asserts"; import { Recorder } from "src/types/collections";
import { RecordersRoles } from "../../shared/payload/constants"; import { isUndefined, isDefined } from "src/utils/asserts";
export const shownOnlyToAdmin = ({ user }: { user: User }): boolean => { export const shownOnlyToAdmin = ({ user }: { user: User }): boolean => {
if (isUndefined(user)) return false; if (isUndefined(user)) return false;

View File

@ -1,7 +1,7 @@
import { FieldAccess } from "payload/types"; import { FieldAccess } from "payload/types";
import { Recorder } from "../../types/collections"; import { RecordersRoles } from "src/shared/payload/constants";
import { isDefined, isUndefined } from "../../utils/asserts"; import { Recorder } from "src/types/collections";
import { RecordersRoles } from "../../shared/payload/constants"; import { isUndefined, isDefined } from "src/utils/asserts";
export const mustBeAdmin: FieldAccess<any, any, Recorder> = ({ req: { user } }): boolean => { export const mustBeAdmin: FieldAccess<any, any, Recorder> = ({ req: { user } }): boolean => {
if (isUndefined(user)) return false; if (isUndefined(user)) return false;

View File

@ -1,6 +1,6 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { AttributeTypes, Collections } from "../../shared/payload/constants"; import { Collections, AttributeTypes } from "src/shared/payload/constants";
export const numberBlock: Block = { export const numberBlock: Block = {
slug: "numberBlock", slug: "numberBlock",

View File

@ -1,6 +1,6 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { Collections, AttributeTypes } from "../../shared/payload/constants"; import { Collections, AttributeTypes } from "src/shared/payload/constants";
export const tagsBlock: Block = { export const tagsBlock: Block = {
slug: "tagsBlock", slug: "tagsBlock",

View File

@ -1,6 +1,6 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { AttributeTypes, Collections } from "../../shared/payload/constants"; import { Collections, AttributeTypes } from "src/shared/payload/constants";
export const textBlock: Block = { export const textBlock: Block = {
slug: "textBlock", slug: "textBlock",

View File

@ -1,5 +1,5 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { BreakBlockType } from "../shared/payload/constants"; import { BreakBlockType } from "src/shared/payload/constants";
export const breakBlock: Block = { export const breakBlock: Block = {
slug: "breakBlock", slug: "breakBlock",

View File

@ -1,5 +1,5 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { createEditor } from "../utils/editor"; import { createEditor } from "src/utils/editor";
export const cueBlock: Block = { export const cueBlock: Block = {
slug: "cueBlock", slug: "cueBlock",

View File

@ -1,5 +1,5 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { createEditor } from "../utils/editor"; import { createEditor } from "src/utils/editor";
export const lineBlock: Block = { export const lineBlock: Block = {
slug: "lineBlock", slug: "lineBlock",

View File

@ -1,7 +1,7 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { createEditor } from "../utils/editor"; import { breakBlock } from "src/blocks/breakBlock";
import { breakBlock } from "./breakBlock"; import { transcriptBlock } from "src/blocks/transcriptBlock";
import { transcriptBlock } from "./transcriptBlock"; import { createEditor } from "src/utils/editor";
const generateRecursiveSectionBlock = (depth = 1, maxDepth = 5): Block => ({ const generateRecursiveSectionBlock = (depth = 1, maxDepth = 5): Block => ({
slug: "sectionBlock", slug: "sectionBlock",

View File

@ -1,6 +1,6 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { cueBlock } from "./cueBlock"; import { cueBlock } from "src/blocks/cueBlock";
import { lineBlock } from "./lineBlock"; import { lineBlock } from "src/blocks/lineBlock";
export const transcriptBlock: Block = { export const transcriptBlock: Block = {
slug: "transcriptBlock", slug: "transcriptBlock",

View File

@ -1,11 +1,11 @@
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { mustBeAdmin } from "../../accesses/fields/mustBeAdmin"; import { mustBeAdmin } from "src/accesses/fields/mustBeAdmin";
import { iconField } from "../../fields/iconField/iconField"; import { iconField } from "src/fields/iconField/iconField";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { slugField } from "../../fields/slugField/slugField"; import { slugField } from "src/fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { Collections, CollectionGroups, AttributeTypes } from "src/shared/payload/constants";
import { Collections, CollectionGroups, AttributeTypes } from "../../shared/payload/constants"; import { buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { const fields = {
slug: "slug", slug: "slug",

View File

@ -1,11 +1,11 @@
import { attributesField } from "../../fields/attributesField/attributesField"; import { attributesField } from "src/fields/attributesField/attributesField";
import { creditsField } from "../../fields/creditsField/creditsField"; import { creditsField } from "src/fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField"; import { imageField } from "src/fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { buildCollectionConfig } from "src/utils/collectionConfig";
import { createEditor } from "../../utils/editor"; import { createEditor } from "src/utils/editor";
import { getByID } from "./endpoints/getByID"; import { getByID } from "./endpoints/getByID";
const fields = { const fields = {
@ -31,7 +31,6 @@ export const Audios = buildCollectionConfig({
admin: { admin: {
group: CollectionGroups.Media, group: CollectionGroups.Media,
preview: ({ id }) => `${process.env.PAYLOAD_PUBLIC_FRONTEND_BASE_URL}/en/audios/${id}`, preview: ({ id }) => `${process.env.PAYLOAD_PUBLIC_FRONTEND_BASE_URL}/en/audios/${id}`,
useAsTitle: fields.filename,
defaultColumns: [ defaultColumns: [
fields.filename, fields.filename,
fields.thumbnail, fields.thumbnail,

View File

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

View File

@ -1,14 +1,4 @@
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import {
QuickFilters,
languageBasedFilters,
publishStatusFilters,
} from "../../components/QuickFilters";
import { creditsField } from "../../fields/creditsField/creditsField";
import { rowField } from "../../fields/rowField/rowField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { createEditor } from "../../utils/editor";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { collectibleBlock } from "./blocks/collectibleBlock"; import { collectibleBlock } from "./blocks/collectibleBlock";
import { pageBlock } from "./blocks/contentBlock"; import { pageBlock } from "./blocks/contentBlock";
import { urlBlock } from "./blocks/urlBlock"; import { urlBlock } from "./blocks/urlBlock";
@ -19,7 +9,17 @@ import { beforeValidatePopulateNameField } from "./hooks/beforeValidatePopulateN
import { validateDate } from "./validations/validateDate"; import { validateDate } from "./validations/validateDate";
import { validateEventsTranslationsDescription } from "./validations/validateEventsTranslationsDescription"; import { validateEventsTranslationsDescription } from "./validations/validateEventsTranslationsDescription";
import { validateEventsTranslationsTitle } from "./validations/validateEventsTranslationsTitle"; import { validateEventsTranslationsTitle } from "./validations/validateEventsTranslationsTitle";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import {
QuickFilters,
languageBasedFilters,
publishStatusFilters,
} from "src/components/QuickFilters";
import { creditsField } from "src/fields/creditsField/creditsField";
import { rowField } from "src/fields/rowField/rowField";
import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { createEditor } from "src/utils/editor";
import { buildVersionedCollectionConfig } from "src/utils/versionedCollectionConfig";
const fields = { const fields = {
name: "name", name: "name",

View File

@ -1,6 +1,6 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { translatedFields } from "../../../fields/translatedFields/translatedFields"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { Collections } from "../../../shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
export const collectibleBlock: Block = { export const collectibleBlock: Block = {
slug: "collectibleBlock", slug: "collectibleBlock",

View File

@ -1,5 +1,5 @@
import { Block } from "payload/types"; import { Block } from "payload/types";
import { Collections } from "../../../shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
export const pageBlock: Block = { export const pageBlock: Block = {
slug: "pageBlock", slug: "pageBlock",

View File

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

View File

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

View File

@ -1,8 +1,8 @@
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "src/endpoints/createStrapiImportEndpoint";
import { Collections } from "../../../shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
import { StrapiLanguage } from "../../../types/strapi"; import { StrapiLanguage } from "src/types/strapi";
import { isDefined, isUndefined } from "../../../utils/asserts"; import { isUndefined, isDefined } from "src/utils/asserts";
import { plainTextToLexical } from "../../../utils/string"; import { plainTextToLexical } from "src/utils/string";
type StrapiChronologyItem = { type StrapiChronologyItem = {
year: number; year: number;

View File

@ -1,6 +1,6 @@
import { FieldHook } from "payload/dist/fields/config/types"; import { FieldHook } from "payload/dist/fields/config/types";
import { ChronologyEvent } from "../../../types/collections"; import { ChronologyEvent } from "src/types/collections";
import { isDefined, isUndefined } from "../../../utils/asserts"; import { isUndefined, isDefined } from "src/utils/asserts";
export const beforeValidatePopulateNameField: FieldHook< export const beforeValidatePopulateNameField: FieldHook<
ChronologyEvent, ChronologyEvent,

View File

@ -1,7 +1,7 @@
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import { Validate } from "payload/types"; import { Validate } from "payload/types";
import { ChronologyEvent } from "../../../types/collections"; import { ChronologyEvent } from "src/types/collections";
import { isUndefined } from "../../../utils/asserts"; import { isUndefined } from "src/utils/asserts";
export const validateDate: Validate<ChronologyEvent["date"] | undefined> = (date) => { export const validateDate: Validate<ChronologyEvent["date"] | undefined> = (date) => {
if (isUndefined(date)) return "This field is required."; if (isUndefined(date)) return "This field is required.";

View File

@ -1,6 +1,6 @@
import { Validate } from "payload/types"; import { Validate } from "payload/types";
import { ChronologyEvent } from "../../../types/collections"; import { ChronologyEvent } from "src/types/collections";
import { isEmpty } from "../../../utils/asserts"; import { isEmpty } from "src/utils/asserts";
export const validateEventsTranslationsDescription: Validate< export const validateEventsTranslationsDescription: Validate<
string | undefined, string | undefined,

View File

@ -1,6 +1,6 @@
import { Validate } from "payload/types"; import { Validate } from "payload/types";
import { ChronologyEvent } from "../../../types/collections"; import { ChronologyEvent } from "src/types/collections";
import { isEmpty } from "../../../utils/asserts"; import { isEmpty } from "src/utils/asserts";
export const validateEventsTranslationsTitle: Validate< export const validateEventsTranslationsTitle: Validate<
string | undefined, string | undefined,

View File

@ -1,29 +1,33 @@
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types"; import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
import { attributesField } from "../../fields/attributesField/attributesField"; import { Where } from "payload/types";
import { componentField } from "../../fields/componentField/componentField";
import { creditsField } from "../../fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField";
import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
import { createEditor } from "../../utils/editor";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { RowLabel } from "./components/RowLabel"; import { RowLabel } from "./components/RowLabel";
import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint"; import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint";
import { getBySlugEndpointGallery } from "./endpoints/getBySlugEndpointGallery"; import { getBySlugEndpointGallery } from "./endpoints/getBySlugEndpointGallery";
import { getBySlugEndpointGalleryImage } from "./endpoints/getBySlugEndpointGalleryImage"; import { getBySlugEndpointGalleryImage } from "./endpoints/getBySlugEndpointGalleryImage";
import { getBySlugEndpointScanPage } from "./endpoints/getBySlugEndpointScanPage"; import { getBySlugEndpointScanPage } from "./endpoints/getBySlugEndpointScanPage";
import { getBySlugEndpointScans } from "./endpoints/getBySlugEndpointScans"; import { getBySlugEndpointScans } from "./endpoints/getBySlugEndpointScans";
import { attributesField } from "src/fields/attributesField/attributesField";
import { backPropagationField } from "src/fields/backPropagationField/backPropagationField";
import { componentField } from "src/fields/componentField/componentField";
import { creditsField } from "src/fields/creditsField/creditsField";
import { imageField } from "src/fields/imageField/imageField";
import { rowField } from "src/fields/rowField/rowField";
import { slugField } from "src/fields/slugField/slugField";
import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "src/hooks/beforeDuplicateAddCopyTo";
import { beforeDuplicatePiping } from "src/hooks/beforeDuplicatePiping";
import { beforeDuplicateUnpublish } from "src/hooks/beforeDuplicateUnpublish";
import { import {
Collections, Collections,
CollectionGroups, CollectionGroups,
CollectibleNature, CollectibleNature,
CollectibleBindingTypes, CollectibleBindingTypes,
CollectiblePageOrders, CollectiblePageOrders,
} from "../../shared/payload/constants"; } from "src/shared/payload/constants";
import { Collectible } from "src/types/collections";
import { isPayloadType } from "src/utils/asserts";
import { createEditor } from "src/utils/editor";
import { buildVersionedCollectionConfig } from "src/utils/versionedCollectionConfig";
const fields = { const fields = {
status: "_status", status: "_status",
@ -691,9 +695,42 @@ 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

@ -1,6 +1,6 @@
import React from "react"; import React from "react";
import { isDefined } from "src/utils/asserts";
import styled from "styled-components"; import styled from "styled-components";
import { isDefined } from "../../../utils/asserts";
interface Props { interface Props {
page?: number; page?: number;

View File

@ -1,41 +1,37 @@
import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; import { convertAudioToEndpointAudioPreview } from "src/collections/Audios/endpoints/getByID";
import { Collections, CollectibleNature } from "../../../shared/payload/constants"; import { convertFileToEndpointFilePreview } from "src/collections/Files/endpoints/getByID";
import { convertPageToEndpointPagePreview } from "src/collections/Pages/endpoints/getBySlugEndpoint";
import { convertRecorderToEndpointRecorderPreview } from "src/collections/Recorders/endpoints/getByID";
import { convertVideoToEndpointVideoPreview } from "src/collections/Videos/endpoints/getByID";
import { createGetByEndpoint } from "src/endpoints/createGetByEndpoint";
import { Collections, CollectibleNature } from "src/shared/payload/constants";
import { EndpointCollectiblePreview, EndpointCollectible } from "src/shared/payload/endpoint-types";
import { Collectible } from "src/types/collections";
import { import {
EndpointCollectiblePreview,
EndpointCollectible,
} from "../../../shared/payload/endpoint-types";
import { Collectible } from "../../../types/collections";
import {
isAudio,
isDefined,
isFile,
isImage, isImage,
isNotEmpty,
isPayloadArrayType,
isPayloadType, isPayloadType,
isNotEmpty,
isDefined,
isPayloadArrayType,
isPublished, isPublished,
isFile,
isScan, isScan,
isAudio,
isVideo, isVideo,
} from "../../../utils/asserts"; } from "src/utils/asserts";
import { import {
convertAttributesToEndpointAttributes,
convertImageToEndpointPayloadImage, convertImageToEndpointPayloadImage,
convertRelationshipsToEndpointRelations, convertAttributesToEndpointAttributes,
convertScanToEndpointScanImage,
getDomainFromUrl, getDomainFromUrl,
} from "../../../utils/endpoints"; convertSourceToEndpointSource,
import { convertAudioToEndpointAudioPreview } from "../../Audios/endpoints/getByID"; convertScanToEndpointScanImage,
import { convertFileToEndpointFilePreview } from "../../Files/endpoints/getByID"; } from "src/utils/endpoints";
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({ export const getBySlugEndpoint = createGetByEndpoint({
collection: Collections.Collectibles, collection: Collections.Collectibles,
attribute: "slug", attribute: "slug",
depth: 3, depth: 3,
handler: async (collectible) => await convertCollectibleToEndpointCollectible(collectible), handler: (collectible) => convertCollectibleToEndpointCollectible(collectible),
}); });
export const convertCollectibleToEndpointCollectiblePreview = ({ export const convertCollectibleToEndpointCollectiblePreview = ({
@ -65,11 +61,8 @@ export const convertCollectibleToEndpointCollectiblePreview = ({
...handlePrice(price, priceEnabled), ...handlePrice(price, priceEnabled),
}); });
const convertCollectibleToEndpointCollectible = async ( const convertCollectibleToEndpointCollectible = (collectible: Collectible): EndpointCollectible => {
collectible: Collectible
): Promise<EndpointCollectible> => {
const { const {
id,
nature, nature,
urls, urls,
subitems, subitems,
@ -84,6 +77,8 @@ const convertCollectibleToEndpointCollectible = async (
weightEnabled, weightEnabled,
pageInfo, pageInfo,
pageInfoEnabled, pageInfoEnabled,
parentItems,
folders,
backgroundImage, backgroundImage,
translations, translations,
scans: rawScans, scans: rawScans,
@ -130,9 +125,7 @@ const convertCollectibleToEndpointCollectible = async (
...(isPayloadType(updatedBy) ...(isPayloadType(updatedBy)
? { updatedBy: convertRecorderToEndpointRecorderPreview(updatedBy) } ? { updatedBy: convertRecorderToEndpointRecorderPreview(updatedBy) }
: {}), : {}),
backlinks: convertRelationshipsToEndpointRelations( parentPages: convertSourceToEndpointSource({ collectibles: parentItems, folders }),
await findIncomingRelationships(Collections.Collectibles, id)
),
}; };
}; };

View File

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

View File

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

View File

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

View File

@ -1,14 +1,14 @@
import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; import { createGetByEndpoint } from "src/endpoints/createGetByEndpoint";
import { Collections } from "../../../shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
import { EndpointCollectibleScans } from "../../../shared/payload/endpoint-types"; import { EndpointCollectibleScans } from "src/shared/payload/endpoint-types";
import { Collectible } from "../../../types/collections"; import { Collectible } from "src/types/collections";
import { isImage, isNotEmpty, isPayloadType, isScan } from "../../../utils/asserts"; import { isPayloadType, isNotEmpty, isImage, isScan } from "src/utils/asserts";
import { import {
convertCreditsToEndpointCredits,
convertImageToEndpointPayloadImage, convertImageToEndpointPayloadImage,
convertSourceToEndpointSource,
convertCreditsToEndpointCredits,
convertScanToEndpointScanImage, convertScanToEndpointScanImage,
} from "../../../utils/endpoints"; } from "src/utils/endpoints";
import { convertCollectibleToEndpointCollectiblePreview } from "./getBySlugEndpoint";
export const getBySlugEndpointScans = createGetByEndpoint({ export const getBySlugEndpointScans = createGetByEndpoint({
collection: Collections.Collectibles, collection: Collections.Collectibles,
@ -29,12 +29,7 @@ export const getBySlugEndpointScans = createGetByEndpoint({
})) ?? [], })) ?? [],
...(isImage(thumbnail) ? { thumbnail: convertImageToEndpointPayloadImage(thumbnail) } : {}), ...(isImage(thumbnail) ? { thumbnail: convertImageToEndpointPayloadImage(thumbnail) } : {}),
...(scansEnabled && scans ? handleScans(scans) : { credits: [], pages: [] }), ...(scansEnabled && scans ? handleScans(scans) : { credits: [], pages: [] }),
backlinks: [ parentPages: convertSourceToEndpointSource({ collectibles: [collectible] }),
{
type: Collections.Collectibles,
value: convertCollectibleToEndpointCollectiblePreview(collectible),
},
],
}; };
}, },
}); });

View File

@ -1,9 +1,9 @@
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { iconField } from "../../fields/iconField/iconField"; import { iconField } from "src/fields/iconField/iconField";
import { slugField } from "../../fields/slugField/slugField"; import { slugField } from "src/fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { const fields = {
slug: "slug", slug: "slug",

View File

@ -1,10 +1,10 @@
import { text } from "payload/dist/fields/validations"; import { text } from "payload/dist/fields/validations";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { getAllEndpoint } from "./endpoints/getAllEndpoint"; import { getAllEndpoint } from "./endpoints/getAllEndpoint";
import { importFromStrapi } from "./endpoints/importFromStrapi"; import { importFromStrapi } from "./endpoints/importFromStrapi";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { shownOnlyToAdmin } from "src/accesses/collections/shownOnlyToAdmin";
import { mustBeAdmin } from "src/accesses/fields/mustBeAdmin";
import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { const fields = {
id: "id", id: "id",

View File

@ -1,7 +1,7 @@
import payload from "payload"; import payload from "payload";
import { Currency } from "../../../types/collections"; import { Collections } from "src/shared/payload/constants";
import { CollectionEndpoint } from "../../../types/payload"; import { Currency } from "src/types/collections";
import { Collections } from "../../../shared/payload/constants"; import { CollectionEndpoint } from "src/types/payload";
export const getAllEndpoint: CollectionEndpoint = { export const getAllEndpoint: CollectionEndpoint = {
method: "get", method: "get",

View File

@ -1,5 +1,5 @@
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "src/endpoints/createStrapiImportEndpoint";
import { Collections } from "../../../shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
type StrapiLanguage = { type StrapiLanguage = {
code: string; code: string;

View File

@ -1,11 +1,11 @@
import { attributesField } from "../../fields/attributesField/attributesField"; import { attributesField } from "src/fields/attributesField/attributesField";
import { creditsField } from "../../fields/creditsField/creditsField"; import { creditsField } from "src/fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField"; import { imageField } from "src/fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { CollectionGroups, Collections } from "../../shared/payload/constants"; import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { buildCollectionConfig } from "src/utils/collectionConfig";
import { createEditor } from "../../utils/editor"; import { createEditor } from "src/utils/editor";
import { getByID } from "./endpoints/getByID"; import { getByID } from "./endpoints/getByID";
const fields = { const fields = {

View File

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

View File

@ -1,10 +1,13 @@
import { iconField } from "../../fields/iconField/iconField"; import { backPropagationField } from "src/fields/backPropagationField/backPropagationField";
import { rowField } from "../../fields/rowField/rowField"; import { iconField } from "src/fields/iconField/iconField";
import { slugField } from "../../fields/slugField/slugField"; import { rowField } from "src/fields/rowField/rowField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { slugField } from "src/fields/slugField/slugField";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { createEditor } from "../../utils/editor"; import { Folder } from "src/types/collections";
import { isPayloadType } from "src/utils/asserts";
import { buildCollectionConfig } from "src/utils/collectionConfig";
import { createEditor } from "src/utils/editor";
import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint"; import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint";
const fields = { const fields = {
@ -41,7 +44,16 @@ export const Folders = buildCollectionConfig({
}, },
endpoints: [getBySlugEndpoint], endpoints: [getBySlugEndpoint],
fields: [ fields: [
rowField([slugField({ name: fields.slug }), iconField({ name: fields.icon })]), rowField([
slugField({ name: fields.slug }),
iconField({ name: fields.icon }),
backPropagationField({
name: fields.parentFolders,
relationTo: Collections.Folders,
hasMany: true,
where: ({ id }) => ({ "sections.subfolders": { equals: id } }),
}),
]),
translatedFields({ translatedFields({
name: fields.translations, name: fields.translations,
admin: { useAsTitle: fields.translationsName }, admin: { useAsTitle: fields.translationsName },
@ -100,4 +112,21 @@ export const Folders = buildCollectionConfig({
hasMany: true, 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,25 +1,24 @@
import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; import { convertAudioToEndpointAudioPreview } from "src/collections/Audios/endpoints/getByID";
import { findIncomingRelationships } from "payloadcms-relationships"; import { convertCollectibleToEndpointCollectiblePreview } from "src/collections/Collectibles/endpoints/getBySlugEndpoint";
import { Collections } from "../../../shared/payload/constants"; import { convertFileToEndpointFilePreview } from "src/collections/Files/endpoints/getByID";
import { EndpointFolderPreview, EndpointFolder } from "../../../shared/payload/endpoint-types"; import { convertImageToEndpointImagePreview } from "src/collections/Images/endpoints/getByID";
import { Folder, Language } from "../../../types/collections"; import { convertPageToEndpointPagePreview } from "src/collections/Pages/endpoints/getBySlugEndpoint";
import { convertVideoToEndpointVideoPreview } from "src/collections/Videos/endpoints/getByID";
import { createGetByEndpoint } from "src/endpoints/createGetByEndpoint";
import { Collections } from "src/shared/payload/constants";
import { EndpointFolderPreview, EndpointFolder } from "src/shared/payload/endpoint-types";
import { Folder, Language } from "src/types/collections";
import { import {
isAudio,
isDefined, isDefined,
isFile,
isImage,
isNotEmpty, isNotEmpty,
isPayloadType, isPayloadType,
isPublished, isPublished,
isImage,
isAudio,
isVideo, isVideo,
} from "../../../utils/asserts"; isFile,
import { convertRelationshipsToEndpointRelations, getLanguageId } from "../../../utils/endpoints"; } from "src/utils/asserts";
import { convertAudioToEndpointAudioPreview } from "../../Audios/endpoints/getByID"; import { getLanguageId, convertSourceToEndpointSource } from "src/utils/endpoints";
import { convertCollectibleToEndpointCollectiblePreview } from "../../Collectibles/endpoints/getBySlugEndpoint";
import { convertFileToEndpointFilePreview } from "../../Files/endpoints/getByID";
import { convertImageToEndpointImagePreview } from "../../Images/endpoints/getByID";
import { convertPageToEndpointPagePreview } from "../../Pages/endpoints/getBySlugEndpoint";
import { convertVideoToEndpointVideoPreview } from "../../Videos/endpoints/getByID";
export const getBySlugEndpoint = createGetByEndpoint({ export const getBySlugEndpoint = createGetByEndpoint({
collection: Collections.Folders, collection: Collections.Folders,
@ -44,8 +43,8 @@ export const convertFolderToEndpointFolderPreview = ({
})) ?? [], })) ?? [],
}); });
const convertFolderToEndpointFolder = async (folder: Folder): Promise<EndpointFolder> => { const convertFolderToEndpointFolder = (folder: Folder): EndpointFolder => {
const { translations, sections, files, id } = folder; const { translations, sections, files, parentFolders } = folder;
return { return {
...convertFolderToEndpointFolderPreview(folder), ...convertFolderToEndpointFolderPreview(folder),
@ -117,9 +116,7 @@ const convertFolderToEndpointFolder = async (folder: Folder): Promise<EndpointFo
return []; return [];
} }
}) ?? [], }) ?? [],
backlinks: convertRelationshipsToEndpointRelations( parentPages: convertSourceToEndpointSource({ folders: parentFolders }),
await findIncomingRelationships(Collections.Folders, id)
),
}; };
}; };

View File

@ -1,10 +1,10 @@
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { QuickFilters, languageBasedFilters } from "../../components/QuickFilters"; import { QuickFilters, languageBasedFilters } from "src/components/QuickFilters";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; import { beforeDuplicateAddCopyTo } from "src/hooks/beforeDuplicateAddCopyTo";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { const fields = {
name: "name", name: "name",

View File

@ -1,15 +1,15 @@
import { createImageSizesRegenerationEndpoint } from "../../endpoints/imageSizesRegenerationEndpoint"; import { createImageSizesRegenerationEndpoint } from "src/endpoints/imageSizesRegenerationEndpoint";
import { attributesField } from "../../fields/attributesField/attributesField"; import { attributesField } from "src/fields/attributesField/attributesField";
import { creditsField } from "../../fields/creditsField/creditsField"; import { creditsField } from "src/fields/creditsField/creditsField";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { Collections } from "../../shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
import { createEditor } from "../../utils/editor"; import { createEditor } from "src/utils/editor";
import { import {
buildImageCollectionConfig, buildImageCollectionConfig,
generateOpenGraphSize, generateOpenGraphSize,
generateWebpSize, generateWebpSize,
} from "../../utils/imageCollectionConfig"; } from "src/utils/imageCollectionConfig";
import { getByID } from "./endpoints/getByID"; import { getByID } from "./endpoints/getByID";
const fields = { const fields = {
@ -36,7 +36,6 @@ export const Images = buildImageCollectionConfig({
admin: { admin: {
preview: ({ id }) => `${process.env.PAYLOAD_PUBLIC_FRONTEND_BASE_URL}/en/images/${id}`, preview: ({ id }) => `${process.env.PAYLOAD_PUBLIC_FRONTEND_BASE_URL}/en/images/${id}`,
defaultColumns: [fields.filename, fields.posts, fields.updatedAt], defaultColumns: [fields.filename, fields.posts, fields.updatedAt],
useAsTitle: fields.filename,
}, },
upload: { upload: {
imageSizes: [ imageSizes: [

View File

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

View File

@ -1,11 +1,10 @@
import { text } from "payload/dist/fields/validations"; import { text } from "payload/dist/fields/validations";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin";
import { rowField } from "../../fields/rowField/rowField";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { getAllEndpoint } from "./endpoints/getAllEndpoint"; import { getAllEndpoint } from "./endpoints/getAllEndpoint";
import { importFromStrapi } from "./endpoints/importFromStrapi"; import { shownOnlyToAdmin } from "src/accesses/collections/shownOnlyToAdmin";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { mustBeAdmin } from "src/accesses/fields/mustBeAdmin";
import { rowField } from "src/fields/rowField/rowField";
import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { const fields = {
id: "id", id: "id",
@ -30,7 +29,7 @@ export const Languages = buildCollectionConfig({
}, },
access: { create: mustBeAdmin, update: mustBeAdmin, delete: mustBeAdmin }, access: { create: mustBeAdmin, update: mustBeAdmin, delete: mustBeAdmin },
timestamps: false, timestamps: false,
endpoints: [importFromStrapi, getAllEndpoint], endpoints: [getAllEndpoint],
fields: [ fields: [
{ {
name: fields.id, name: fields.id,

View File

@ -1,7 +1,7 @@
import payload from "payload"; import payload from "payload";
import { Language } from "../../../types/collections"; import { Collections } from "src/shared/payload/constants";
import { CollectionEndpoint } from "../../../types/payload"; import { Language } from "src/types/collections";
import { Collections } from "../../../shared/payload/constants"; import { CollectionEndpoint } from "src/types/payload";
export const getAllEndpoint: CollectionEndpoint = { export const getAllEndpoint: CollectionEndpoint = {
method: "get", method: "get",

View File

@ -1,18 +0,0 @@
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint";
import { Collections } from "../../../shared/payload/constants";
type StrapiLanguage = {
name: string;
code: string;
};
export const importFromStrapi = createStrapiImportEndpoint<StrapiLanguage>({
strapi: {
collection: "languages",
params: {},
},
payload: {
collection: Collections.Languages,
convert: ({ code, name }) => ({ id: code, name }),
},
});

View File

@ -1,11 +1,11 @@
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin"; import { shownOnlyToAdmin } from "src/accesses/collections/shownOnlyToAdmin";
import { createImageSizesRegenerationEndpoint } from "../../endpoints/imageSizesRegenerationEndpoint"; import { createImageSizesRegenerationEndpoint } from "src/endpoints/imageSizesRegenerationEndpoint";
import { Collections } from "../../shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
import { import {
buildImageCollectionConfig, buildImageCollectionConfig,
generateOpenGraphSize, generateOpenGraphSize,
generateWebpSize, generateWebpSize,
} from "../../utils/imageCollectionConfig"; } from "src/utils/imageCollectionConfig";
const fields = { const fields = {
filename: "filename", filename: "filename",

View File

@ -1,20 +1,22 @@
import { breakBlock } from "../../blocks/breakBlock"; import { Where } from "payload/types";
import { sectionBlock } from "../../blocks/sectionBlock";
import { transcriptBlock } from "../../blocks/transcriptBlock";
import { QuickFilters, publishStatusFilters } from "../../components/QuickFilters";
import { attributesField } from "../../fields/attributesField/attributesField";
import { creditsField } from "../../fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField";
import { slugField } from "../../fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
import { beforeDuplicatePiping } from "../../hooks/beforeDuplicatePiping";
import { beforeDuplicateUnpublish } from "../../hooks/beforeDuplicateUnpublish";
import { createEditor } from "../../utils/editor";
import { buildVersionedCollectionConfig } from "../../utils/versionedCollectionConfig";
import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint"; import { getBySlugEndpoint } from "./endpoints/getBySlugEndpoint";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { breakBlock } from "src/blocks/breakBlock";
import { sectionBlock } from "src/blocks/sectionBlock";
import { transcriptBlock } from "src/blocks/transcriptBlock";
import { QuickFilters, publishStatusFilters } from "src/components/QuickFilters";
import { attributesField } from "src/fields/attributesField/attributesField";
import { backPropagationField } from "src/fields/backPropagationField/backPropagationField";
import { creditsField } from "src/fields/creditsField/creditsField";
import { imageField } from "src/fields/imageField/imageField";
import { rowField } from "src/fields/rowField/rowField";
import { slugField } from "src/fields/slugField/slugField";
import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "src/hooks/beforeDuplicateAddCopyTo";
import { beforeDuplicatePiping } from "src/hooks/beforeDuplicatePiping";
import { beforeDuplicateUnpublish } from "src/hooks/beforeDuplicateUnpublish";
import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { createEditor } from "src/utils/editor";
import { buildVersionedCollectionConfig } from "src/utils/versionedCollectionConfig";
const fields = { const fields = {
slug: "slug", slug: "slug",
@ -29,7 +31,8 @@ const fields = {
summary: "summary", summary: "summary",
content: "content", content: "content",
credits: "credits", credits: "credits",
sourceUrls: "sourceUrls", collectibles: "collectibles",
folders: "folders",
} as const satisfies Record<string, string>; } as const satisfies Record<string, string>;
export const Pages = buildVersionedCollectionConfig({ export const Pages = buildVersionedCollectionConfig({
@ -41,7 +44,13 @@ export const Pages = buildVersionedCollectionConfig({
defaultSort: fields.slug, defaultSort: fields.slug,
admin: { admin: {
useAsTitle: fields.slug, useAsTitle: fields.slug,
defaultColumns: [fields.slug, fields.thumbnail, fields.backgroundImage, fields.translations], defaultColumns: [
fields.slug,
fields.thumbnail,
fields.backgroundImage,
fields.translations,
fields.folders,
],
group: CollectionGroups.Collections, group: CollectionGroups.Collections,
preview: ({ slug }) => `${process.env.PAYLOAD_PUBLIC_FRONTEND_BASE_URL}/en/pages/${slug}`, preview: ({ slug }) => `${process.env.PAYLOAD_PUBLIC_FRONTEND_BASE_URL}/en/pages/${slug}`,
components: { components: {
@ -114,18 +123,31 @@ export const Pages = buildVersionedCollectionConfig({
}), }),
}, },
creditsField({ name: fields.credits }), creditsField({ name: fields.credits }),
{
name: fields.sourceUrls,
label: "Source URLs",
type: "text",
hasMany: true,
admin: {
description:
"If the content originates from an external source (e.g: fandom.com, an online interview...) you can add a link to the original page(s) here",
width: "50%",
},
},
], ],
}), }),
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,28 +1,26 @@
import { createGetByEndpoint } from "../../../endpoints/createGetByEndpoint"; import { convertRecorderToEndpointRecorderPreview } from "src/collections/Recorders/endpoints/getByID";
import { findIncomingRelationships } from "payloadcms-relationships"; import { createGetByEndpoint } from "src/endpoints/createGetByEndpoint";
import { Collections, BreakBlockType } from "../../../shared/payload/constants"; import { Collections, BreakBlockType } from "src/shared/payload/constants";
import { import {
EndpointPagePreview, EndpointPagePreview,
EndpointPage, EndpointPage,
TableOfContentEntry, TableOfContentEntry,
} from "../../../shared/payload/endpoint-types"; } from "src/shared/payload/endpoint-types";
import { import {
RichTextContent, RichTextContent,
isNodeBlockNode, isNodeBlockNode,
isBlockNodeSectionBlock, isBlockNodeSectionBlock,
isBlockNodeBreakBlock, isBlockNodeBreakBlock,
} from "../../../shared/payload/rich-text"; } from "src/shared/payload/rich-text";
import { Page } from "../../../types/collections"; import { Page } from "src/types/collections";
import { isImage, isNotEmpty, isPayloadType } from "../../../utils/asserts"; import { isImage, isPayloadType, isNotEmpty } from "src/utils/asserts";
import { import {
convertAttributesToEndpointAttributes,
convertCreditsToEndpointCredits,
convertImageToEndpointPayloadImage, convertImageToEndpointPayloadImage,
convertAttributesToEndpointAttributes,
convertRTCToEndpointRTC, convertRTCToEndpointRTC,
convertRelationshipsToEndpointRelations, convertCreditsToEndpointCredits,
getDomainFromUrl, convertSourceToEndpointSource,
} from "../../../utils/endpoints"; } from "src/utils/endpoints";
import { convertRecorderToEndpointRecorderPreview } from "../../Recorders/endpoints/getByID";
export const getBySlugEndpoint = createGetByEndpoint({ export const getBySlugEndpoint = createGetByEndpoint({
collection: Collections.Pages, collection: Collections.Pages,
@ -51,8 +49,8 @@ export const convertPageToEndpointPagePreview = ({
updatedAt, updatedAt,
}); });
const convertPageToEndpointPage = async (page: Page): Promise<EndpointPage> => { const convertPageToEndpointPage = (page: Page): EndpointPage => {
const { translations, backgroundImage, createdAt, updatedBy, id } = page; const { translations, collectibles, folders, backgroundImage, createdAt, updatedBy } = page;
return { return {
...convertPageToEndpointPagePreview(page), ...convertPageToEndpointPagePreview(page),
@ -60,17 +58,7 @@ const convertPageToEndpointPage = async (page: Page): Promise<EndpointPage> => {
? { backgroundImage: convertImageToEndpointPayloadImage(backgroundImage) } ? { backgroundImage: convertImageToEndpointPayloadImage(backgroundImage) }
: {}), : {}),
translations: translations.map( translations: translations.map(
({ ({ content, language, sourceLanguage, title, pretitle, subtitle, summary, credits }) => ({
content,
language,
sourceLanguage,
title,
pretitle,
subtitle,
summary,
credits,
sourceUrls,
}) => ({
language: isPayloadType(language) ? language.id : language, language: isPayloadType(language) ? language.id : language,
sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage, sourceLanguage: isPayloadType(sourceLanguage) ? sourceLanguage.id : sourceLanguage,
...(isNotEmpty(pretitle) ? { pretitle } : {}), ...(isNotEmpty(pretitle) ? { pretitle } : {}),
@ -80,20 +68,13 @@ const convertPageToEndpointPage = async (page: Page): Promise<EndpointPage> => {
content: convertRTCToEndpointRTC(content), content: convertRTCToEndpointRTC(content),
toc: handleToc(content), toc: handleToc(content),
credits: convertCreditsToEndpointCredits(credits), credits: convertCreditsToEndpointCredits(credits),
sourceUrls:
sourceUrls?.map((url) => ({
url,
label: getDomainFromUrl(url),
})) ?? [],
}) })
), ),
createdAt, createdAt,
...(isPayloadType(updatedBy) ...(isPayloadType(updatedBy)
? { updatedBy: convertRecorderToEndpointRecorderPreview(updatedBy) } ? { updatedBy: convertRecorderToEndpointRecorderPreview(updatedBy) }
: {}), : {}),
backlinks: convertRelationshipsToEndpointRelations( parentPages: convertSourceToEndpointSource({ collectibles, folders }),
await findIncomingRelationships(Collections.Pages, id)
),
}; };
}; };

View File

@ -1,16 +1,16 @@
import { mustBeAdmin as mustBeAdminForCollections } from "../../accesses/collections/mustBeAdmin"; import { mustBeAdminOrSelf } from "src/accesses/collections/mustBeAdminOrSelf";
import { mustBeAdminOrSelf } from "../../accesses/collections/mustBeAdminOrSelf"; import { QuickFilters } from "src/components/QuickFilters";
import { mustBeAdmin as mustBeAdminForFields } from "../../accesses/fields/mustBeAdmin"; import { imageField } from "src/fields/imageField/imageField";
import { QuickFilters } from "../../components/QuickFilters"; import { rowField } from "src/fields/rowField/rowField";
import { imageField } from "../../fields/imageField/imageField"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { rowField } from "../../fields/rowField/rowField"; import { Collections, CollectionGroups, RecordersRoles } from "src/shared/payload/constants";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { buildCollectionConfig } from "src/utils/collectionConfig";
import { Collections, CollectionGroups, RecordersRoles } from "../../shared/payload/constants"; import { createEditor } from "src/utils/editor";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { createEditor } from "../../utils/editor";
import { getByID } from "./endpoints/getByID"; import { getByID } from "./endpoints/getByID";
import { importFromStrapi } from "./endpoints/importFromStrapi"; import { importFromStrapi } from "./endpoints/importFromStrapi";
import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole"; import { beforeLoginMustHaveAtLeastOneRole } from "./hooks/beforeLoginMustHaveAtLeastOneRole";
import { mustBeAdmin as mustBeAdminForCollections } from "src/accesses/collections/mustBeAdmin";
import { mustBeAdmin as mustBeAdminForFields } from "src/accesses/fields/mustBeAdmin";
const fields = { const fields = {
username: "username", username: "username",

View File

@ -1,13 +1,10 @@
import payload from "payload"; import payload from "payload";
import { Recorder } from "../../../types/collections"; import { Collections } from "src/shared/payload/constants";
import { CollectionEndpoint } from "../../../types/payload"; import { EndpointRecorderPreview, EndpointRecorder } from "src/shared/payload/endpoint-types";
import { isImage, isPayloadType } from "../../../utils/asserts"; import { Recorder } from "src/types/collections";
import { import { CollectionEndpoint } from "src/types/payload";
convertImageToEndpointPayloadImage, import { isPayloadType, isImage } from "src/utils/asserts";
convertRTCToEndpointRTC, import { convertImageToEndpointPayloadImage, convertRTCToEndpointRTC } from "src/utils/endpoints";
} from "../../../utils/endpoints";
import { Collections } from "../../../shared/payload/constants";
import { EndpointRecorderPreview, EndpointRecorder } from "../../../shared/payload/endpoint-types";
export const getByID: CollectionEndpoint = { export const getByID: CollectionEndpoint = {
method: "get", method: "get",

View File

@ -1,10 +1,10 @@
import payload from "payload"; import payload from "payload";
import { createStrapiImportEndpoint } from "../../../endpoints/createStrapiImportEndpoint"; import { createStrapiImportEndpoint } from "src/endpoints/createStrapiImportEndpoint";
import { Recorder } from "../../../types/collections"; import { Collections } from "src/shared/payload/constants";
import { StrapiImage, StrapiLanguage } from "../../../types/strapi"; import { Recorder } from "src/types/collections";
import { isDefined } from "../../../utils/asserts"; import { StrapiImage, StrapiLanguage } from "src/types/strapi";
import { uploadStrapiImage } from "../../../utils/localApi"; import { isDefined } from "src/utils/asserts";
import { Collections } from "../../../shared/payload/constants"; import { uploadStrapiImage } from "src/utils/localApi";
type StrapiRecorder = { type StrapiRecorder = {
username: string; username: string;

View File

@ -1,7 +1,7 @@
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin"; import { shownOnlyToAdmin } from "src/accesses/collections/shownOnlyToAdmin";
import { createImageSizesRegenerationEndpoint } from "../../endpoints/imageSizesRegenerationEndpoint"; import { createImageSizesRegenerationEndpoint } from "src/endpoints/imageSizesRegenerationEndpoint";
import { Collections } from "../../shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
import { buildImageCollectionConfig, generateWebpSize } from "../../utils/imageCollectionConfig"; import { buildImageCollectionConfig, generateWebpSize } from "src/utils/imageCollectionConfig";
const fields = { const fields = {
filename: "filename", filename: "filename",
@ -17,7 +17,6 @@ export const Scans = buildImageCollectionConfig({
plural: "Scans", plural: "Scans",
}, },
admin: { admin: {
useAsTitle: fields.filename,
defaultColumns: [fields.filename, fields.updatedAt], defaultColumns: [fields.filename, fields.updatedAt],
hidden: shownOnlyToAdmin, hidden: shownOnlyToAdmin,
}, },

View File

@ -1,10 +1,10 @@
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { slugField } from "../../fields/slugField/slugField"; import { slugField } from "src/fields/slugField/slugField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo"; import { beforeDuplicateAddCopyTo } from "src/hooks/beforeDuplicateAddCopyTo";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { const fields = {
slug: "slug", slug: "slug",

View File

@ -1,12 +1,14 @@
import { attributesField } from "../../fields/attributesField/attributesField"; import { attributesField } from "src/fields/attributesField/attributesField";
import { componentField } from "../../fields/componentField/componentField"; import { componentField } from "src/fields/componentField/componentField";
import { creditsField } from "../../fields/creditsField/creditsField"; import { creditsField } from "src/fields/creditsField/creditsField";
import { imageField } from "../../fields/imageField/imageField"; import { imageField } from "src/fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField"; import { rowField } from "src/fields/rowField/rowField";
import { translatedFields } from "../../fields/translatedFields/translatedFields"; import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { Video } from "src/types/collections";
import { createEditor } from "../../utils/editor"; import { isPayloadType } from "src/utils/asserts";
import { buildCollectionConfig } from "src/utils/collectionConfig";
import { createEditor } from "src/utils/editor";
import { getByID } from "./endpoints/getByID"; import { getByID } from "./endpoints/getByID";
const fields = { const fields = {
@ -38,7 +40,6 @@ export const Videos = buildCollectionConfig({
labels: { singular: "Video", plural: "Videos" }, labels: { singular: "Video", plural: "Videos" },
defaultSort: fields.filename, defaultSort: fields.filename,
admin: { admin: {
useAsTitle: fields.filename,
group: CollectionGroups.Media, group: CollectionGroups.Media,
preview: ({ id }) => `${process.env.PAYLOAD_PUBLIC_FRONTEND_BASE_URL}/en/videos/${id}`, preview: ({ id }) => `${process.env.PAYLOAD_PUBLIC_FRONTEND_BASE_URL}/en/videos/${id}`,
defaultColumns: [ defaultColumns: [
@ -128,4 +129,12 @@ export const Videos = buildCollectionConfig({
], ],
}), }),
], ],
custom: {
getBackPropagatedRelationships: ({ platform, platformEnabled }: Video) => {
if (!platform || !platformEnabled) {
return [];
}
return [isPayloadType(platform.channel) ? platform.channel.id : platform.channel];
},
},
}); });

View File

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

View File

@ -1,7 +1,8 @@
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { rowField } from "../../fields/rowField/rowField"; import { backPropagationField } from "src/fields/backPropagationField/backPropagationField";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { rowField } from "src/fields/rowField/rowField";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { const fields = {
url: "url", url: "url",
@ -30,5 +31,11 @@ export const VideosChannels: CollectionConfig = buildCollectionConfig({
{ name: fields.title, type: "text", required: true }, { name: fields.title, type: "text", required: true },
{ name: fields.subscribers, type: "number", 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

@ -1,18 +1,13 @@
import { shownOnlyToAdmin } from "../../accesses/collections/shownOnlyToAdmin"; import { shownOnlyToAdmin } from "src/accesses/collections/shownOnlyToAdmin";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { buildCollectionConfig } from "../../utils/collectionConfig"; import { buildCollectionConfig } from "src/utils/collectionConfig";
export const VideosSubtitles = buildCollectionConfig({ export const VideosSubtitles = buildCollectionConfig({
slug: Collections.VideosSubtitles, slug: Collections.VideosSubtitles,
labels: { singular: "Video Subtitle", plural: "Videos Subtitles" }, labels: { singular: "Video Subtitle", plural: "Videos Subtitles" },
admin: { admin: { group: CollectionGroups.Media, disableDuplicate: true, hidden: shownOnlyToAdmin },
useAsTitle: "filename",
group: CollectionGroups.Media,
disableDuplicate: true,
hidden: shownOnlyToAdmin,
},
upload: { upload: {
// mimeTypes: ["text/*"], Disable because of a bug on Chrome windows where the MIME is detected as application/octet-stream instead of text/vtt or text/plain mimeTypes: ["text/*"],
disableLocalStorage: true, disableLocalStorage: true,
}, },
timestamps: false, timestamps: false,

View File

@ -1,10 +1,10 @@
import { GlobalConfig } from "payload/types"; import { GlobalConfig } from "payload/types";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { imageField } from "../../fields/imageField/imageField";
import { rowField } from "../../fields/rowField/rowField";
import { getConfigEndpoint } from "./endpoints/getConfigEndpoint"; import { getConfigEndpoint } from "./endpoints/getConfigEndpoint";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { mustBeAdmin } from "src/accesses/fields/mustBeAdmin";
import { globalAfterChangeSendChangesWebhook } from "../../hooks/afterOperationSendChangesWebhook"; import { imageField } from "src/fields/imageField/imageField";
import { rowField } from "src/fields/rowField/rowField";
import { globalAfterChangeWebhook } from "src/hooks/afterOperationWebhook";
import { Collections, CollectionGroups } from "src/shared/payload/constants";
const fields = { const fields = {
homeBackgroundImage: "homeBackgroundImage", homeBackgroundImage: "homeBackgroundImage",
@ -32,7 +32,7 @@ export const WebsiteConfig: GlobalConfig = {
access: { update: mustBeAdmin, read: mustBeAdmin }, access: { update: mustBeAdmin, read: mustBeAdmin },
endpoints: [getConfigEndpoint], endpoints: [getConfigEndpoint],
hooks: { hooks: {
afterChange: [globalAfterChangeSendChangesWebhook], afterChange: [globalAfterChangeWebhook],
}, },
fields: [ fields: [
rowField([ rowField([

View File

@ -1,10 +1,10 @@
import payload from "payload"; import payload from "payload";
import { CollectionEndpoint } from "../../../types/payload"; import { convertFolderToEndpointFolderPreview } from "src/collections/Folders/endpoints/getBySlugEndpoint";
import { isImage, isPayloadType } from "../../../utils/asserts"; import { Collections } from "src/shared/payload/constants";
import { convertImageToEndpointPayloadImage } from "../../../utils/endpoints"; import { EndpointWebsiteConfig } from "src/shared/payload/endpoint-types";
import { convertFolderToEndpointFolderPreview } from "../../Folders/endpoints/getBySlugEndpoint"; import { CollectionEndpoint } from "src/types/payload";
import { Collections } from "../../../shared/payload/constants"; import { isImage, isPayloadType } from "src/utils/asserts";
import { EndpointWebsiteConfig } from "../../../shared/payload/endpoint-types"; import { convertImageToEndpointPayloadImage } from "src/utils/endpoints";
export const getConfigEndpoint: CollectionEndpoint = { export const getConfigEndpoint: CollectionEndpoint = {
method: "get", method: "get",

View File

@ -1,12 +1,12 @@
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { mustBeAdmin } from "../../accesses/collections/mustBeAdmin";
import { QuickFilters, languageBasedFilters } from "../../components/QuickFilters";
import { rowField } from "../../fields/rowField/rowField";
import { translatedFields } from "../../fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "../../hooks/beforeDuplicateAddCopyTo";
import { buildCollectionConfig } from "../../utils/collectionConfig";
import { getAllEndpoint } from "./endpoints/getAllEndpoint"; import { getAllEndpoint } from "./endpoints/getAllEndpoint";
import { Collections, CollectionGroups } from "../../shared/payload/constants"; import { mustBeAdmin } from "src/accesses/fields/mustBeAdmin";
import { QuickFilters, languageBasedFilters } from "src/components/QuickFilters";
import { rowField } from "src/fields/rowField/rowField";
import { translatedFields } from "src/fields/translatedFields/translatedFields";
import { beforeDuplicateAddCopyTo } from "src/hooks/beforeDuplicateAddCopyTo";
import { Collections, CollectionGroups } from "src/shared/payload/constants";
import { buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { const fields = {
name: "name", name: "name",

View File

@ -1,8 +1,8 @@
import payload from "payload"; import payload from "payload";
import { CollectionEndpoint } from "../../../types/payload"; import { Collections } from "src/shared/payload/constants";
import { isPayloadType } from "../../../utils/asserts"; import { EndpointWording } from "src/shared/payload/endpoint-types";
import { Collections } from "../../../shared/payload/constants"; import { CollectionEndpoint } from "src/types/payload";
import { EndpointWording } from "../../../shared/payload/endpoint-types"; import { isPayloadType } from "src/utils/asserts";
export const getAllEndpoint: CollectionEndpoint = { export const getAllEndpoint: CollectionEndpoint = {
method: "get", method: "get",

View File

@ -2,8 +2,8 @@ import { Options } from "payload/dist/collections/operations/local/find";
import QueryString from "qs"; import QueryString from "qs";
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { LanguageCodes } from "src/shared/payload/constants";
import styled from "styled-components"; import styled from "styled-components";
import { LanguageCodes } from "../shared/payload/constants";
type Props = { type Props = {
slug: string; slug: string;

View File

@ -1,6 +1,6 @@
import payload, { GeneratedTypes } from "payload"; import payload, { GeneratedTypes } from "payload";
import { CollectionEndpoint } from "../types/payload"; import { CollectionEndpoint } from "src/types/payload";
import { isPublished } from "../utils/asserts"; import { isPublished } from "src/utils/asserts";
interface Params<C extends keyof GeneratedTypes["collections"], R> { interface Params<C extends keyof GeneratedTypes["collections"], R> {
collection: C; collection: C;

View File

@ -1,8 +1,8 @@
import payload, { GeneratedTypes } from "payload"; import payload, { GeneratedTypes } from "payload";
import QueryString from "qs"; import QueryString from "qs";
import { Recorder } from "../types/collections"; import { Recorder } from "src/types/collections";
import { CollectionEndpoint } from "../types/payload"; import { CollectionEndpoint } from "src/types/payload";
import { isDefined } from "../utils/asserts"; import { isDefined } from "src/utils/asserts";
const getAllStrapiEntries = async (collectionSlug: string, params: Object): Promise<any[]> => { const getAllStrapiEntries = async (collectionSlug: string, params: Object): Promise<any[]> => {
let page = 1; let page = 1;

View File

@ -1,27 +1,11 @@
import payload from "payload"; import payload from "payload";
import { Endpoint } from "payload/config"; import { Endpoint } from "payload/config";
import { Collections } from "../shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
import { EndpointChange } from "../shared/payload/webhooks"; import { EndpointAllIds } from "src/shared/payload/endpoint-types";
import {
getEndpointChangesForAudio,
getEndpointChangesForChronologyEvent,
getEndpointChangesForCollectible,
getEndpointChangesForCurrency,
getEndpointChangesForFile,
getEndpointChangesForFolder,
getEndpointChangesForImage,
getEndpointChangesForLanguage,
getEndpointChangesForPage,
getEndpointChangesForRecorder,
getEndpointChangesForVideo,
getEndpointChangesForWebsiteConfig,
getEndpointChangesForWording,
} from "../hooks/afterOperationSendChangesWebhook";
import { uniqueBy } from "../utils/array";
export const getAllEndpoint: Endpoint = { export const getAllIds: Endpoint = {
method: "get", method: "get",
path: "/all", path: "/all-ids",
handler: async (req, res) => { handler: async (req, res) => {
if (!req.user) { if (!req.user) {
return res.status(403).send({ return res.status(403).send({
@ -111,22 +95,18 @@ export const getAllEndpoint: Endpoint = {
}, },
}); });
const result: EndpointChange[] = [ const result: EndpointAllIds = {
...getEndpointChangesForWebsiteConfig(), collectibles: { slugs: collectibles.docs.map(({ slug }) => slug) },
...getEndpointChangesForLanguage(), pages: { slugs: pages.docs.map(({ slug }) => slug) },
...getEndpointChangesForCurrency(), folders: { slugs: folders.docs.map(({ slug }) => slug) },
...getEndpointChangesForWording(), videos: { ids: videos.docs.map(({ id }) => id) },
...folders.docs.flatMap(getEndpointChangesForFolder), audios: { ids: audios.docs.map(({ id }) => id) },
...pages.docs.flatMap(getEndpointChangesForPage), images: { ids: images.docs.map(({ id }) => id) },
...collectibles.docs.flatMap(getEndpointChangesForCollectible), files: { ids: files.docs.map(({ id }) => id) },
...audios.docs.flatMap(getEndpointChangesForAudio), recorders: { ids: recorders.docs.map(({ id }) => id) },
...images.docs.flatMap(getEndpointChangesForImage), chronologyEvents: { ids: chronologyEvents.docs.map(({ id }) => id) },
...videos.docs.flatMap(getEndpointChangesForVideo), };
...files.docs.flatMap(getEndpointChangesForFile),
...recorders.docs.flatMap(getEndpointChangesForRecorder),
...chronologyEvents.docs.flatMap(getEndpointChangesForChronologyEvent),
];
return res.status(200).send(uniqueBy(result, ({ url }) => url)); return res.status(200).send(result);
}, },
}; };

View File

@ -0,0 +1,208 @@
import payload from "payload";
import { Endpoint } from "payload/config";
import { Collections } from "src/shared/payload/constants";
import { EndpointAllSDKUrls } from "src/shared/payload/endpoint-types";
import { getSDKEndpoint } from "src/shared/payload/sdk";
import { Collectible } from "src/types/collections";
export const getAllSDKUrlsEndpoint: Endpoint = {
method: "get",
path: "/all-sdk-urls",
handler: async (req, res) => {
if (!req.user) {
return res.status(403).send({
errors: [
{
message: "You are not allowed to perform this action.",
},
],
});
}
const collectibles = await payload.find({
collection: Collections.Collectibles,
depth: 0,
pagination: false,
user: req.user,
where: {
_status: {
equals: "published",
},
},
});
const pages = await payload.find({
collection: Collections.Pages,
depth: 0,
pagination: false,
user: req.user,
where: {
_status: {
equals: "published",
},
},
});
const folders = await payload.find({
collection: Collections.Folders,
depth: 0,
pagination: false,
user: req.user,
});
const videos = await payload.find({
collection: Collections.Videos,
depth: 0,
pagination: false,
user: req.user,
});
const audios = await payload.find({
collection: Collections.Audios,
depth: 0,
pagination: false,
user: req.user,
});
const images = await payload.find({
collection: Collections.Images,
depth: 0,
pagination: false,
user: req.user,
});
const files = await payload.find({
collection: Collections.Files,
depth: 0,
pagination: false,
user: req.user,
});
const recorders = await payload.find({
collection: Collections.Recorders,
depth: 0,
pagination: false,
user: req.user,
});
const chronologyEvents = await payload.find({
collection: Collections.ChronologyEvents,
depth: 0,
pagination: false,
user: req.user,
where: {
_status: {
equals: "published",
},
},
});
const urls = new Set([
getSDKEndpoint.getConfigEndpoint(),
getSDKEndpoint.getLanguagesEndpoint(),
getSDKEndpoint.getCurrenciesEndpoint(),
getSDKEndpoint.getWordingsEndpoint(),
...folders.docs.flatMap((doc) => getSDKUrlsForDocument(Collections.Folders, doc)),
...pages.docs.flatMap((doc) => getSDKUrlsForDocument(Collections.Pages, doc)),
...chronologyEvents.docs.flatMap((doc) =>
getSDKUrlsForDocument(Collections.ChronologyEvents, doc)
),
...videos.docs.flatMap((doc) => getSDKUrlsForDocument(Collections.Videos, doc)),
...audios.docs.flatMap((doc) => getSDKUrlsForDocument(Collections.Audios, doc)),
...images.docs.flatMap((doc) => getSDKUrlsForDocument(Collections.Images, doc)),
...files.docs.flatMap((doc) => getSDKUrlsForDocument(Collections.Files, doc)),
...collectibles.docs.flatMap((doc) => getSDKUrlsForDocument(Collections.Collectibles, doc)),
...recorders.docs.flatMap((doc) => getSDKUrlsForDocument(Collections.Recorders, doc)),
]);
const result: EndpointAllSDKUrls = {
urls: [...urls],
};
return res.status(200).send(result);
},
};
export const getSDKUrlsForDocument = (collection: Collections, doc: any): string[] => {
switch (collection) {
case Collections.WebsiteConfig:
return [getSDKEndpoint.getConfigEndpoint()];
case Collections.Folders:
return [getSDKEndpoint.getFolderEndpoint(doc.slug)];
case Collections.Languages:
return [getSDKEndpoint.getLanguagesEndpoint()];
case Collections.Currencies:
return [getSDKEndpoint.getCurrenciesEndpoint()];
case Collections.Wordings:
return [getSDKEndpoint.getWordingsEndpoint()];
case Collections.Pages:
return [getSDKEndpoint.getPageEndpoint(doc.slug)];
case Collections.Collectibles: {
const { slug, gallery, scans, scansEnabled } = doc as Collectible;
const urls: string[] = [getSDKEndpoint.getCollectibleEndpoint(slug)];
if (gallery && gallery.length > 0) {
urls.push(getSDKEndpoint.getCollectibleGalleryEndpoint(slug));
urls.push(
...gallery.map((_, index) =>
getSDKEndpoint.getCollectibleGalleryImageEndpoint(slug, index.toString())
)
);
}
if (scans && scansEnabled) {
urls.push(getSDKEndpoint.getCollectibleScansEndpoint(slug));
// TODO: Add other pages for cover, obi, dustjacket...
if (scans.pages) {
urls.push(
...scans.pages.map(({ page }) =>
getSDKEndpoint.getCollectibleScanPageEndpoint(slug, page.toString())
)
);
}
}
return urls;
}
case Collections.ChronologyEvents:
return [
getSDKEndpoint.getChronologyEventsEndpoint(),
getSDKEndpoint.getChronologyEventByIDEndpoint(doc.id),
];
case Collections.Images:
return [getSDKEndpoint.getImageByIDEndpoint(doc.id)];
case Collections.Audios:
return [getSDKEndpoint.getAudioByIDEndpoint(doc.id)];
case Collections.Videos:
return [getSDKEndpoint.getVideoByIDEndpoint(doc.id)];
case Collections.Recorders:
return [getSDKEndpoint.getRecorderByIDEndpoint(doc.id)];
case Collections.Files:
return [getSDKEndpoint.getFileByIDEndpoint(doc.id)];
case Collections.Attributes:
case Collections.CreditsRole:
case Collections.GenericContents:
case Collections.MediaThumbnails:
case Collections.Scans:
case Collections.Tags:
case Collections.VideosChannels:
case Collections.VideosSubtitles:
return [];
default: {
console.warn("Unrecognized collection", collection);
return [];
}
}
};

View File

@ -1,5 +1,5 @@
import payload from "payload"; import payload from "payload";
import { CollectionEndpoint } from "../types/payload"; import { CollectionEndpoint } from "src/types/payload";
export const createImageSizesRegenerationEndpoint = ( export const createImageSizesRegenerationEndpoint = (
collection: "images" | "scans" | "media-thumbnails" collection: "images" | "scans" | "media-thumbnails"

View File

@ -1,7 +1,7 @@
import { BlockField } from "payload/dist/fields/config/types"; import { BlockField } from "payload/dist/fields/config/types";
import { numberBlock } from "../../blocks/attributeBlocks/numberBlock"; import { numberBlock } from "src/blocks/attributeBlocks/numberBlock";
import { tagsBlock } from "../../blocks/attributeBlocks/tagsBlock"; import { tagsBlock } from "src/blocks/attributeBlocks/tagsBlock";
import { textBlock } from "../../blocks/attributeBlocks/textBlock"; import { textBlock } from "src/blocks/attributeBlocks/textBlock";
type AttributesFieldProps = Omit<BlockField, "type" | "blocks">; type AttributesFieldProps = Omit<BlockField, "type" | "blocks">;

View File

@ -0,0 +1,51 @@
import payload, { GeneratedTypes } from "payload";
import { FieldBase, SingleRelationshipField } from "payload/dist/fields/config/types";
import { Where } from "payload/types";
import { isEmpty } from "src/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

@ -0,0 +1,47 @@
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,5 +1,5 @@
import { CollapsibleField, Condition, Field } from "payload/types"; import { CollapsibleField, Condition, Field } from "payload/types";
import { capitalize } from "../../utils/string"; import { capitalize } from "src/utils/string";
type Props = { type Props = {
name: string; name: string;

View File

@ -1,9 +1,9 @@
import { array } from "payload/dist/fields/validations"; import { array } from "payload/dist/fields/validations";
import { ArrayField } from "payload/types"; import { ArrayField } from "payload/types";
import { Credits } from "../../types/collections"; import { rowField } from "src/fields/rowField/rowField";
import { hasDuplicates, isDefined, isPayloadType, isUndefined } from "../../utils/asserts"; import { Collections } from "src/shared/payload/constants";
import { rowField } from "../rowField/rowField"; import { Credits } from "src/types/collections";
import { Collections } from "../../shared/payload/constants"; import { isDefined, isUndefined, isPayloadType, hasDuplicates } from "src/utils/asserts";
type Props = Omit<ArrayField, "type" | "fields">; type Props = Omit<ArrayField, "type" | "fields">;

View File

@ -1,6 +1,6 @@
import { icons } from "@iconify-json/material-symbols"; import { icons } from "@iconify-json/material-symbols";
import { TextField } from "payload/types"; import { TextField } from "payload/types";
import { isEmpty } from "../../utils/asserts"; import { isEmpty } from "src/utils/asserts";
type Props = Omit<TextField, "type" | "hasMany" | "maxRows" | "minRows">; type Props = Omit<TextField, "type" | "hasMany" | "maxRows" | "minRows">;

View File

@ -1,7 +1,7 @@
import { Props } from "payload/components/views/Cell"; import { Props } from "payload/components/views/Cell";
import React, { useEffect, useMemo, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { isUndefined } from "../../utils/asserts"; import { isUndefined } from "src/utils/asserts";
export const Cell = ({ cellData, field, rowData, collection }: Props): JSX.Element => { export const Cell = ({ cellData, field, rowData, collection }: Props): JSX.Element => {
const [imageURL, setImageURL] = useState<string>(); const [imageURL, setImageURL] = useState<string>();

View File

@ -1,5 +1,5 @@
import { TextField } from "payload/types"; import { TextField } from "payload/types";
import { isUndefined } from "../../utils/asserts"; import { isUndefined } from "src/utils/asserts";
type Props = Omit<TextField, "type" | "hasMany" | "minRows" | "maxRows">; type Props = Omit<TextField, "type" | "hasMany" | "minRows" | "maxRows">;

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { Language } from "../../types/collections"; import { Language } from "src/types/collections";
import { isDefined } from "../../utils/asserts"; import { isDefined } from "src/utils/asserts";
import { formatLanguageCode } from "../../utils/string"; import { formatLanguageCode } from "src/utils/string";
interface Props { interface Props {
cellData: { language?: string | Language; title?: string }[]; cellData: { language?: string | Language; title?: string }[];

View File

@ -1,8 +1,8 @@
import React from "react"; import React from "react";
import { Language } from "src/types/collections";
import { isDefined } from "src/utils/asserts";
import { formatLanguageCode, shortenEllipsis } from "src/utils/string";
import styled from "styled-components"; import styled from "styled-components";
import { Language } from "../../types/collections";
import { isDefined } from "../../utils/asserts";
import { formatLanguageCode, shortenEllipsis } from "../../utils/string";
interface Props { interface Props {
language?: Language | string; language?: Language | string;

View File

@ -1,11 +1,11 @@
import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types"; import { RowLabelArgs } from "payload/dist/admin/components/forms/RowLabel/types";
import { array } from "payload/dist/fields/validations"; import { array } from "payload/dist/fields/validations";
import { ArrayField, Field } from "payload/types"; import { ArrayField, Field } from "payload/types";
import { hasDuplicates, isDefined, isUndefined } from "../../utils/asserts";
import { rowField } from "../rowField/rowField";
import { Cell } from "./Cell"; import { Cell } from "./Cell";
import { RowLabel } from "./RowLabel"; import { RowLabel } from "./RowLabel";
import { Collections } from "../../shared/payload/constants"; import { rowField } from "src/fields/rowField/rowField";
import { Collections } from "src/shared/payload/constants";
import { isDefined, isUndefined, hasDuplicates } from "src/utils/asserts";
const fieldsNames = { const fieldsNames = {
language: "language", language: "language",

View File

@ -1,432 +0,0 @@
import { Collections } from "../shared/payload/constants";
import { SDKEndpointNames, getSDKEndpoint } from "../shared/payload/sdk";
import { EndpointChange } from "../shared/payload/webhooks";
import {
Audio,
ChronologyEvent,
Collectible,
File,
Folder,
Image,
Page,
Recorder,
Relationship,
Video,
} from "../types/collections";
import { isDefined, isPayloadType } from "../utils/asserts";
import {
AfterChangeHook,
AfterDeleteHook,
BeforeChangeHook,
BeforeDeleteHook,
} from "payload/dist/collections/config/types";
import { GeneratedTypes } from "payload";
import { uniqueBy } from "../utils/array";
import { GlobalAfterChangeHook } from "payload/types";
import { findRelationByID } from "payloadcms-relationships/dist/utils";
export const beforeChangePrepareChanges: BeforeChangeHook = async ({
collection,
originalDoc,
context,
data,
}) => {
if ("_status" in data && data._status === "draft") return data;
if (!originalDoc) return data;
context.beforeChangeChanges = await getChanges(
collection.slug as keyof GeneratedTypes["collections"],
originalDoc
);
return data;
};
export const afterChangeSendChangesWebhook: AfterChangeHook = async ({
doc,
collection,
context,
}) => {
if ("_status" in doc && doc._status === "draft") return doc;
const changes = await getChanges(collection.slug as keyof GeneratedTypes["collections"], doc);
const previousChanges = context.beforeChangeChanges as EndpointChange[] | undefined;
if (isDefined(previousChanges)) {
await sendWebhookMessage(uniqueBy([...previousChanges, ...changes], ({ url }) => url));
} else {
await sendWebhookMessage(changes);
}
return doc;
};
export const beforeDeletePrepareChanges: BeforeDeleteHook = async ({ id, collection, context }) => {
context.beforeDeleteChanges = await getChanges(
collection.slug as keyof GeneratedTypes["collections"],
{ id }
);
};
export const afterDeleteSendChangesWebhook: AfterDeleteHook = async ({ doc, context }) => {
const changes = context.beforeDeleteChanges as EndpointChange[] | undefined;
if (isDefined(changes)) {
await sendWebhookMessage(changes);
}
return doc;
};
export const globalAfterChangeSendChangesWebhook: GlobalAfterChangeHook = async ({
doc,
global,
}) => {
const changes: EndpointChange[] = [];
switch (global.slug as keyof GeneratedTypes["globals"]) {
case Collections.WebsiteConfig:
changes.push(...getEndpointChangesForWebsiteConfig());
break;
default:
break;
}
await sendWebhookMessage(uniqueBy(changes, ({ url }) => url));
return doc;
};
const getChanges = async (
slug: keyof GeneratedTypes["collections"],
doc: any
): Promise<EndpointChange[]> => {
if (slug === "relationships") return [];
if (slug === "payload-migrations") return [];
if (slug === "payload-preferences") return [];
let relation: Relationship;
try {
relation = await findRelationByID(slug, doc.id);
} catch (e) {
relation = {
id: "",
document: {
relationTo: slug,
value: doc,
},
outgoingRelations: [],
};
}
const changes: EndpointChange[] = getEndpointChangesFromDocument(relation.document);
relation.incomingRelations?.forEach((relation) =>
changes.push(...getEndpointChangesFromIncomingRelation(relation))
);
relation.outgoingRelations?.forEach((relation) =>
changes.push(...getEndpointChangesFromOutgoingRelation(relation))
);
return uniqueBy(changes, ({ url }) => url);
};
// -------------------------------------------------------------------------------------------------
const getEndpointChangesFromDocument = ({
relationTo,
value,
}: NonNullable<Relationship["document"]>): EndpointChange[] => {
if (!isPayloadType(value)) return [];
switch (relationTo) {
case Collections.Folders:
return getEndpointChangesForFolder(value);
case Collections.Pages:
return getEndpointChangesForPage(value);
case Collections.Collectibles:
return getEndpointChangesForCollectible(value);
case Collections.Audios:
return getEndpointChangesForAudio(value);
case Collections.Images:
return getEndpointChangesForImage(value);
case Collections.Videos:
return getEndpointChangesForVideo(value);
case Collections.Files:
return getEndpointChangesForFile(value);
case Collections.Recorders:
return getEndpointChangesForRecorder(value);
case Collections.ChronologyEvents:
return getEndpointChangesForChronologyEvent(value);
case Collections.Languages:
return getEndpointChangesForLanguage();
case Collections.Currencies:
return getEndpointChangesForCurrency();
case Collections.Wordings:
return getEndpointChangesForWording();
case Collections.Attributes:
case Collections.CreditsRole:
case Collections.GenericContents:
case Collections.MediaThumbnails:
case Collections.Scans:
case Collections.Tags:
case Collections.VideosChannels:
case Collections.VideosSubtitles:
default:
return [];
}
};
const getEndpointChangesFromIncomingRelation = ({
relationTo,
value,
}: NonNullable<Relationship["incomingRelations"]>[number]): EndpointChange[] => {
if (!isPayloadType(value)) return [];
switch (relationTo) {
case Collections.Folders:
return getEndpointChangesForFolder(value);
case Collections.Pages:
return getEndpointChangesForPage(value);
case Collections.Collectibles:
return getEndpointChangesForCollectible(value);
case Collections.Audios:
return getEndpointChangesForAudio(value);
case Collections.Images:
return getEndpointChangesForImage(value);
case Collections.Videos:
return getEndpointChangesForVideo(value);
case Collections.Files:
return getEndpointChangesForFile(value);
case Collections.Recorders:
return getEndpointChangesForRecorder(value);
case Collections.ChronologyEvents:
return getEndpointChangesForChronologyEvent(value);
case Collections.Languages:
case Collections.Currencies:
case Collections.Wordings:
case Collections.Attributes:
case Collections.CreditsRole:
case Collections.GenericContents:
case Collections.MediaThumbnails:
case Collections.Scans:
case Collections.Tags:
case Collections.VideosChannels:
case Collections.VideosSubtitles:
default:
return [];
}
};
const getEndpointChangesFromOutgoingRelation = ({
relationTo,
value,
}: NonNullable<Relationship["outgoingRelations"]>[number]): EndpointChange[] => {
if (!isPayloadType(value)) return [];
switch (relationTo) {
case Collections.Folders:
return getEndpointChangesForFolder(value);
case Collections.Pages:
return getEndpointChangesForPage(value);
case Collections.Collectibles:
return getEndpointChangesForCollectible(value);
case Collections.Audios:
return getEndpointChangesForAudio(value);
case Collections.Images:
return getEndpointChangesForImage(value);
case Collections.Videos:
return getEndpointChangesForVideo(value);
case Collections.Files:
return getEndpointChangesForFile(value);
case Collections.Languages:
case Collections.Currencies:
case Collections.Wordings:
case Collections.Attributes:
case Collections.CreditsRole:
case Collections.GenericContents:
case Collections.MediaThumbnails:
case Collections.Scans:
case Collections.Tags:
case Collections.VideosChannels:
case Collections.VideosSubtitles:
case Collections.ChronologyEvents:
case Collections.Recorders:
default:
return [];
}
};
export const getEndpointChangesForWebsiteConfig = (): EndpointChange[] => [
{
type: SDKEndpointNames.getWebsiteConfig,
url: getSDKEndpoint.getWebsiteConfig(),
},
];
export const getEndpointChangesForFolder = ({ slug }: Folder): EndpointChange[] => [
{ type: SDKEndpointNames.getFolder, slug, url: getSDKEndpoint.getFolder(slug) },
];
export const getEndpointChangesForLanguage = (): EndpointChange[] => [
{ type: SDKEndpointNames.getLanguages, url: getSDKEndpoint.getLanguages() },
];
export const getEndpointChangesForCurrency = (): EndpointChange[] => [
{ type: SDKEndpointNames.getCurrencies, url: getSDKEndpoint.getCurrencies() },
];
export const getEndpointChangesForWording = (): EndpointChange[] => [
{ type: SDKEndpointNames.getWordings, url: getSDKEndpoint.getWordings() },
];
export const getEndpointChangesForPage = ({ slug }: Page): EndpointChange[] => [
{ type: SDKEndpointNames.getPage, slug, url: getSDKEndpoint.getPage(slug) },
];
export const getEndpointChangesForCollectible = ({
slug,
gallery,
scans,
scansEnabled,
}: Collectible): EndpointChange[] => {
const changes: EndpointChange[] = [];
changes.push({
type: SDKEndpointNames.getCollectible,
slug,
url: getSDKEndpoint.getCollectible(slug),
});
if (gallery && gallery.length > 0) {
changes.push({
type: SDKEndpointNames.getCollectibleGallery,
slug,
url: getSDKEndpoint.getCollectibleGallery(slug),
});
gallery.forEach((_, indexNumber) => {
const index = indexNumber.toString();
changes.push({
type: SDKEndpointNames.getCollectibleGalleryImage,
slug,
index: index,
url: getSDKEndpoint.getCollectibleGalleryImage(slug, index),
});
});
}
if (scans && scansEnabled) {
changes.push({
type: SDKEndpointNames.getCollectibleScans,
slug,
url: getSDKEndpoint.getCollectibleScans(slug),
});
// TODO: Add other changes for cover, obi, dustjacket...
scans.pages?.forEach(({ page }) => {
const index = page.toString();
changes.push({
type: SDKEndpointNames.getCollectibleScanPage,
slug,
index: index,
url: getSDKEndpoint.getCollectibleScanPage(slug, index),
});
});
}
return changes;
};
export const getEndpointChangesForAudio = ({ id }: Audio): EndpointChange[] => [
{ type: SDKEndpointNames.getAudioByID, id, url: getSDKEndpoint.getAudioByID(id) },
];
export const getEndpointChangesForImage = ({ id }: Image): EndpointChange[] => [
{ type: SDKEndpointNames.getImageByID, id, url: getSDKEndpoint.getImageByID(id) },
];
export const getEndpointChangesForVideo = ({ id }: Video): EndpointChange[] => [
{ type: SDKEndpointNames.getVideoByID, id, url: getSDKEndpoint.getVideoByID(id) },
];
export const getEndpointChangesForFile = ({ id }: File): EndpointChange[] => [
{ type: SDKEndpointNames.getFileByID, id, url: getSDKEndpoint.getFileByID(id) },
];
export const getEndpointChangesForRecorder = ({ id }: Recorder): EndpointChange[] => [
{ type: SDKEndpointNames.getRecorderByID, id, url: getSDKEndpoint.getRecorderByID(id) },
];
export const getEndpointChangesForChronologyEvent = ({ id }: ChronologyEvent): EndpointChange[] => [
{
type: SDKEndpointNames.getChronologyEventByID,
id,
url: getSDKEndpoint.getChronologyEventByID(id),
},
{
type: SDKEndpointNames.getChronologyEvents,
url: getSDKEndpoint.getChronologyEvents(),
},
];
// -------------------------------------------------------------------------------------------------
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 (changes: EndpointChange[]) => {
if (changes.length === 0) return;
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(changes),
method: "POST",
});
})
);
} catch (e) {
if (e instanceof Error) {
console.warn("Error while sending webhook", e.message);
} else {
console.warn("Error while sending webhook", e);
}
}
};

View File

@ -0,0 +1,81 @@
import {
AfterDeleteHook,
AfterChangeHook as CollectionAfterChangeHook,
} from "payload/dist/collections/config/types";
import { AfterChangeHook as GlobalAfterChangeHook } from "payload/dist/globals/config/types";
import { getSDKUrlsForDocument } from "src/endpoints/getAllSDKUrlsEndpoint";
import { getAddedBackPropagationRelationships } from "src/fields/backPropagationField/backPropagationUtils";
import { Collections } from "src/shared/payload/constants";
import { AfterOperationWebHookMessage } from "src/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 sendWebhookMessage = async (message: AfterOperationWebHookMessage) => {
await fetch(`${process.env.WEB_HOOK_URI}/collection-operation`, {
headers: {
Authorization: `Bearer ${process.env.WEB_HOOK_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify(message),
method: "POST",
}).catch((e) => {
console.warn("Error while sending webhook", e);
});
};

View File

@ -1,5 +1,5 @@
import { BeforeDuplicate } from "payload/types"; import { BeforeDuplicate } from "payload/types";
import { CollectionStatus } from "../shared/payload/constants"; import { CollectionStatus } from "src/shared/payload/constants";
export const beforeDuplicateUnpublish: BeforeDuplicate = ({ data }) => ({ export const beforeDuplicateUnpublish: BeforeDuplicate = ({ data }) => ({
...data, ...data,

View File

@ -4,35 +4,34 @@ import { cloudStorage } from "@payloadcms/plugin-cloud-storage";
import path from "path"; import path from "path";
import { buildConfig } from "payload/config"; import { buildConfig } from "payload/config";
import { sftpAdapter } from "payloadcms-sftp-storage"; import { sftpAdapter } from "payloadcms-sftp-storage";
import { Attributes } from "./collections/Attributes/Attributes"; import { Attributes } from "src/collections/Attributes/Attributes";
import { Audios } from "./collections/Audios/Audios"; import { Audios } from "src/collections/Audios/Audios";
import { ChronologyEvents } from "./collections/ChronologyEvents/ChronologyEvents"; import { ChronologyEvents } from "src/collections/ChronologyEvents/ChronologyEvents";
import { Collectibles } from "./collections/Collectibles/Collectibles"; import { Collectibles } from "src/collections/Collectibles/Collectibles";
import { CreditsRoles } from "./collections/CreditsRoles/CreditsRoles"; import { CreditsRoles } from "src/collections/CreditsRoles/CreditsRoles";
import { Currencies } from "./collections/Currencies/Currencies"; import { Currencies } from "src/collections/Currencies/Currencies";
import { Files } from "./collections/Files/Files"; import { Files } from "src/collections/Files/Files";
import { Folders } from "./collections/Folders/Folders"; import { Folders } from "src/collections/Folders/Folders";
import { GenericContents } from "./collections/GenericContents/GenericContents"; import { GenericContents } from "src/collections/GenericContents/GenericContents";
import { Images } from "./collections/Images/Images"; import { Images } from "src/collections/Images/Images";
import { Languages } from "./collections/Languages/Languages"; import { Languages } from "src/collections/Languages/Languages";
import { MediaThumbnails } from "./collections/MediaThumbnails/MediaThumbnails"; import { MediaThumbnails } from "src/collections/MediaThumbnails/MediaThumbnails";
import { Pages } from "./collections/Pages/Pages"; import { Pages } from "src/collections/Pages/Pages";
import { Recorders } from "./collections/Recorders/Recorders"; import { Recorders } from "src/collections/Recorders/Recorders";
import { Scans } from "./collections/Scans/Scans"; import { Scans } from "src/collections/Scans/Scans";
import { Tags } from "./collections/Tags/Tags"; import { Tags } from "src/collections/Tags/Tags";
import { Videos } from "./collections/Videos/Videos"; import { Videos } from "src/collections/Videos/Videos";
import { VideosChannels } from "./collections/VideosChannels/VideosChannels"; import { VideosChannels } from "src/collections/VideosChannels/VideosChannels";
import { VideosSubtitles } from "./collections/VideosSubtitles/VideosSubtitles"; import { VideosSubtitles } from "src/collections/VideosSubtitles/VideosSubtitles";
import { WebsiteConfig } from "./collections/WebsiteConfig/WebsiteConfig"; import { WebsiteConfig } from "src/collections/WebsiteConfig/WebsiteConfig";
import { Wordings } from "./collections/Wordings/Wordings"; import { Wordings } from "src/collections/Wordings/Wordings";
import { Icon } from "./components/Icon"; import { Icon } from "src/components/Icon";
import { Logo } from "./components/Logo"; import { Logo } from "src/components/Logo";
import { getAllEndpoint } from "./endpoints/getAllEndpoint"; import { getAllIds } from "src/endpoints/getAllIdsEndpoint";
import { createEditor } from "./utils/editor"; import { getAllSDKUrlsEndpoint } from "src/endpoints/getAllSDKUrlsEndpoint";
import { Collections } from "./shared/payload/constants"; import { Collections } from "src/shared/payload/constants";
import { relationshipsPlugin } from "payloadcms-relationships"; import { createEditor } from "src/utils/editor";
import { shownOnlyToAdmin } from "./accesses/collections/shownOnlyToAdmin"; // import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
import { mustBeAdmin } from "./accesses/fields/mustBeAdmin";
const configuredSftpAdapter = sftpAdapter({ const configuredSftpAdapter = sftpAdapter({
connectOptions: { connectOptions: {
@ -56,6 +55,22 @@ export default buildConfig({
}, },
css: path.resolve(__dirname, "styles.scss"), css: path.resolve(__dirname, "styles.scss"),
bundler: webpackBundler(), bundler: webpackBundler(),
webpack: (config) => ({
...config,
resolve: {
...config.resolve,
// plugins: [
// ...(config.resolve?.plugins ?? []),
// new TsconfigPathsPlugin({
// /* options: see below */
// }),
// ],
alias: {
...config.resolve?.alias,
"src/": path.resolve(__dirname, "src"),
},
},
}),
}, },
editor: createEditor({}), editor: createEditor({}),
collections: [ collections: [
@ -90,7 +105,7 @@ export default buildConfig({
typescript: { typescript: {
outputFile: path.resolve(__dirname, "types/collections.ts"), outputFile: path.resolve(__dirname, "types/collections.ts"),
}, },
endpoints: [getAllEndpoint], endpoints: [getAllSDKUrlsEndpoint, getAllIds],
graphQL: { graphQL: {
disable: true, disable: true,
}, },
@ -98,20 +113,6 @@ export default buildConfig({
skip: () => true, skip: () => true,
}, },
plugins: [ plugins: [
relationshipsPlugin({
// rebuildOnInit: true,
collectionConfig: {
admin: {
hidden: shownOnlyToAdmin,
},
access: {
update: mustBeAdmin,
create: mustBeAdmin,
delete: mustBeAdmin,
},
},
}),
cloudStorage({ cloudStorage({
collections: { collections: {
[Collections.Videos]: { [Collections.Videos]: {

View File

@ -1,9 +1,8 @@
import "dotenv/config"; import "dotenv/config";
import express from "express"; import express from "express";
import path from "path";
import payload from "payload"; import payload from "payload";
import { isUndefined } from "./utils/asserts"; import { Collections, RecordersRoles } from "src/shared/payload/constants";
import { Collections, RecordersRoles } from "./shared/payload/constants"; import { isUndefined, isDefined } from "src/utils/asserts";
const app = express(); const app = express();
@ -28,36 +27,32 @@ const start = async () => {
express: app, express: app,
onInit: async () => { onInit: async () => {
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`); payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
const recorders = await payload.find({ collection: Collections.Recorders });
const seedFirstUser = async () => { // If no recorders, we seed some initial data
const recorders = await payload.find({ collection: Collections.Recorders }); 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");
if (recorders.docs.length > 0) return; await payload.create({
if (isUndefined(process.env.SEEDING_ADMIN_EMAIL)) return; collection: Collections.Recorders,
if (isUndefined(process.env.SEEDING_ADMIN_PASSWORD)) return; data: {
if (isUndefined(process.env.SEEDING_ADMIN_USERNAME)) return; email: process.env.SEEDING_ADMIN_EMAIL,
password: process.env.SEEDING_ADMIN_PASSWORD,
payload.logger.info("Seeding your first user"); username: process.env.SEEDING_ADMIN_USERNAME,
role: [RecordersRoles.Admin, RecordersRoles.Api],
await payload.create({ anonymize: false,
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();
}, },
}); });
// Add your own express routes here
app.use("/public", express.static(path.join(__dirname, "../public")));
app.get("/robots.txt", (_, res) => { app.get("/robots.txt", (_, res) => {
res.type("text/plain"); res.type("text/plain");
res.send("User-agent: *\nDisallow: /"); res.send("User-agent: *\nDisallow: /");

@ -1 +0,0 @@
Subproject commit c2702ea508dde59264ee66100054bacc0834c029

View File

@ -40,7 +40,6 @@ export interface Config {
currencies: Currency; currencies: Currency;
wordings: Wording; wordings: Wording;
"generic-contents": GenericContent; "generic-contents": GenericContent;
relationships: Relationship;
"payload-preferences": PayloadPreference; "payload-preferences": PayloadPreference;
"payload-migrations": PayloadMigration; "payload-migrations": PayloadMigration;
}; };
@ -95,9 +94,10 @@ export interface Page {
[k: string]: unknown; [k: string]: unknown;
}; };
credits?: Credits; credits?: Credits;
sourceUrls?: string[] | null;
id?: string | null; id?: string | null;
}[]; }[];
folders?: (string | Folder)[] | null;
collectibles?: (string | Collectible)[] | null;
updatedBy: string | Recorder; updatedBy: string | Recorder;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
@ -352,6 +352,79 @@ export interface Recorder {
lockUntil?: string | null; lockUntil?: string | null;
password?: 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 * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "collectibles". * via the `definition` "collectibles".
@ -535,6 +608,8 @@ export interface Collectible {
id?: string | null; id?: string | null;
}[] }[]
| null; | null;
folders?: (string | Folder)[] | null;
parentItems?: (string | Collectible)[] | null;
updatedBy: string | Recorder; updatedBy: string | Recorder;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
@ -876,78 +951,7 @@ export interface VideosChannel {
url: string; url: string;
title: string; title: string;
subscribers: number; 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 * This interface was referenced by `Config`'s JSON-Schema
@ -1078,262 +1082,6 @@ export interface Wording {
updatedAt: string; updatedAt: string;
createdAt: 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;
}
)[]
| null;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences". * via the `definition` "payload-preferences".

View File

@ -1,9 +0,0 @@
export const uniqueBy = <T, K extends string | number>(array: T[], getKey: (item: T) => K) => {
const alreadyFoundKeys: K[] = [];
return array.filter((item) => {
var currentItemKey = getKey(item);
if (alreadyFoundKeys.includes(currentItemKey)) return false;
alreadyFoundKeys.push(currentItemKey);
return true;
});
};

View File

@ -1,6 +1,7 @@
import { PayloadImage, PayloadMedia } from "../shared/payload/endpoint-types"; import { PayloadImage, PayloadMedia } from "src/shared/payload/endpoint-types";
import { RichTextContent, isNodeParagraphNode } from "../shared/payload/rich-text"; import { RichTextContent, isNodeParagraphNode } from "src/shared/payload/rich-text";
import { Audio, File, Image, MediaThumbnail, Scan, Video } from "../types/collections"; import { Scan, MediaThumbnail, Video, Image, Audio } from "src/types/collections";
import { File } from "src/types/collections";
export const isDefined = <T>(value: T | null | undefined): value is T => export const isDefined = <T>(value: T | null | undefined): value is T =>
value !== null && value !== undefined; value !== null && value !== undefined;

View File

@ -1,21 +1,20 @@
import { GeneratedTypes } from "payload"; import { GeneratedTypes } from "payload";
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { formatToPascalCase } from "./string"; import { collectionAfterChangeWebhook, afterDeleteWebhook } from "src/hooks/afterOperationWebhook";
import { import { formatToPascalCase } from "src/utils/string";
afterChangeSendChangesWebhook,
afterDeleteSendChangesWebhook,
beforeChangePrepareChanges,
beforeDeletePrepareChanges,
} from "../hooks/afterOperationSendChangesWebhook";
type CollectionConfigWithPlugins = CollectionConfig; type CollectionConfigWithPlugins = CollectionConfig;
export type BuildCollectionConfig = Omit< export type BuildCollectionConfig = Omit<
CollectionConfigWithPlugins, CollectionConfigWithPlugins,
"slug" | "typescript" | "labels" "slug" | "typescript" | "labels" | "custom"
> & { > & {
slug: keyof GeneratedTypes["collections"]; slug: keyof GeneratedTypes["collections"];
labels: { singular: string; plural: string }; labels: { singular: string; plural: string };
custom?: {
getBackPropagatedRelationships?: (object: any) => string[];
[key: string]: unknown;
};
}; };
export const buildCollectionConfig = (config: BuildCollectionConfig): CollectionConfig => ({ export const buildCollectionConfig = (config: BuildCollectionConfig): CollectionConfig => ({
@ -23,9 +22,7 @@ export const buildCollectionConfig = (config: BuildCollectionConfig): Collection
typescript: { interface: formatToPascalCase(config.labels.singular) }, typescript: { interface: formatToPascalCase(config.labels.singular) },
hooks: { hooks: {
...config.hooks, ...config.hooks,
beforeChange: [...(config.hooks?.beforeChange ?? []), beforeChangePrepareChanges], afterChange: [...(config.hooks?.afterChange ?? []), collectionAfterChangeWebhook],
afterChange: [...(config.hooks?.afterChange ?? []), afterChangeSendChangesWebhook], afterDelete: [...(config.hooks?.afterDelete ?? []), afterDeleteWebhook],
beforeDelete: [...(config.hooks?.beforeDelete ?? []), beforeDeletePrepareChanges],
afterDelete: [...(config.hooks?.afterDelete ?? []), afterDeleteSendChangesWebhook],
}, },
}); });

View File

@ -1,23 +1,19 @@
import { convertAudioToEndpointAudioPreview } from "../collections/Audios/endpoints/getByID"; import { convertAudioToEndpointAudioPreview } from "src/collections/Audios/endpoints/getByID";
import { convertEventToEndpointEvent } from "../collections/ChronologyEvents/endpoints/getAllEndpoint"; import { convertImageToEndpointImagePreview } from "src/collections/Images/endpoints/getByID";
import { convertCollectibleToEndpointCollectiblePreview } from "../collections/Collectibles/endpoints/getBySlugEndpoint"; import { convertRecorderToEndpointRecorderPreview } from "src/collections/Recorders/endpoints/getByID";
import { convertFileToEndpointFilePreview } from "../collections/Files/endpoints/getByID"; import { convertVideoToEndpointVideoPreview } from "src/collections/Videos/endpoints/getByID";
import { convertFolderToEndpointFolderPreview } from "../collections/Folders/endpoints/getBySlugEndpoint"; import { AttributeTypes } from "src/shared/payload/constants";
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, Collections } from "../shared/payload/constants";
import { import {
EndpointTag, EndpointTag,
EndpointSource,
EndpointSourcePreview,
EndpointRole, EndpointRole,
EndpointCredit, EndpointCredit,
EndpointAttribute, EndpointAttribute,
PayloadImage, PayloadImage,
EndpointScanImage, EndpointScanImage,
EndpointPayloadImage, EndpointPayloadImage,
EndpointRelation, } from "src/shared/payload/endpoint-types";
} from "../shared/payload/endpoint-types";
import { import {
RichTextContent, RichTextContent,
isNodeBlockNode, isNodeBlockNode,
@ -30,33 +26,35 @@ import {
isUploadNodeImageNode, isUploadNodeImageNode,
isUploadNodeAudioNode, isUploadNodeAudioNode,
isUploadNodeVideoNode, isUploadNodeVideoNode,
} from "../shared/payload/rich-text"; } from "src/shared/payload/rich-text";
import { import {
Audio,
Credits,
CreditsRole,
Image, Image,
Language, Audio,
MediaThumbnail,
NumberBlock,
Relationship,
Scan,
Tag,
TagsBlock,
TextBlock,
Video, Video,
} from "../types/collections"; Collectible,
Folder,
Language,
CreditsRole,
Credits,
TagsBlock,
NumberBlock,
TextBlock,
Scan,
MediaThumbnail,
Tag,
} from "src/types/collections";
import { import {
isPayloadType,
isImage,
isAudio, isAudio,
isVideo,
isNotEmpty,
isPayloadArrayType,
isPublished,
isDefined, isDefined,
isEmpty, isEmpty,
isFile,
isImage,
isPayloadArrayType,
isPayloadImage, isPayloadImage,
isPayloadType, } from "src/utils/asserts";
isVideo,
} from "./asserts";
const convertTagToEndpointTag = ({ id, slug, page, translations }: Tag): EndpointTag => ({ const convertTagToEndpointTag = ({ id, slug, page, translations }: Tag): EndpointTag => ({
id, id,
@ -141,64 +139,86 @@ export const convertRTCToEndpointRTC = (
}; };
}; };
export const convertRelationshipsToEndpointRelations = ( // TODO: Handle URL sources
relationships: Relationship["incomingRelations"] | Relationship["outgoingRelations"] export const convertSourceToEndpointSource = ({
): EndpointRelation[] => collectibles,
relationships?.flatMap<EndpointRelation>(({ relationTo, value }) => { folders,
if (!isPayloadType(value)) return []; gallery,
switch (relationTo) { scans,
case Collections.Folders: }: {
return { type: Collections.Folders, value: convertFolderToEndpointFolderPreview(value) }; collectibles?: (string | Collectible)[] | null | undefined;
scans?: (string | Collectible)[] | null | undefined;
gallery?: (string | Collectible)[] | null | undefined;
folders?: (string | Folder)[] | null | undefined;
}): EndpointSource[] => {
const result: EndpointSource[] = [];
case Collections.Pages: const convertFolderToEndpointSourcePreview = ({
return { type: Collections.Pages, value: convertPageToEndpointPagePreview(value) }; id,
slug,
translations,
}: Folder): EndpointSourcePreview => ({
id,
slug,
translations: translations.map(({ language, name }) => ({
language: isPayloadType(language) ? language.id : language,
title: name,
})),
});
case Collections.Collectibles: const convertCollectibleToEndpointSourcePreview = ({
return { id,
type: Collections.Collectibles, slug,
value: convertCollectibleToEndpointCollectiblePreview(value), 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.Images: if (collectibles && isPayloadArrayType(collectibles)) {
if (!isImage(value)) return []; collectibles.filter(isPublished).forEach((collectible) => {
return { type: Collections.Images, value: convertImageToEndpointImagePreview(value) }; result.push({
type: "collectible",
collectible: convertCollectibleToEndpointSourcePreview(collectible),
});
});
}
case Collections.Videos: if (scans && isPayloadArrayType(scans)) {
if (!isVideo(value)) return []; scans.filter(isPublished).forEach((collectible) => {
return { type: Collections.Videos, value: convertVideoToEndpointVideoPreview(value) }; result.push({
type: "scans",
collectible: convertCollectibleToEndpointSourcePreview(collectible),
});
});
}
case Collections.Audios: if (gallery && isPayloadArrayType(gallery)) {
if (!isAudio(value)) return []; gallery.filter(isPublished).forEach((collectible) => {
return { type: Collections.Audios, value: convertAudioToEndpointAudioPreview(value) }; result.push({
type: "gallery",
collectible: convertCollectibleToEndpointSourcePreview(collectible),
});
});
}
case Collections.Files: if (folders && isPayloadArrayType(folders)) {
if (!isFile(value)) return []; folders.forEach((folder) => {
return { type: Collections.Files, value: convertFileToEndpointFilePreview(value) }; result.push({
type: "folder",
folder: convertFolderToEndpointSourcePreview(folder),
});
});
}
case Collections.Recorders: return result;
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 => { export const getDomainFromUrl = (url: string): string => {
const urlObject = new URL(url); const urlObject = new URL(url);

View File

@ -1,8 +1,8 @@
import { ImageSize } from "payload/dist/uploads/types"; import { ImageSize } from "payload/dist/uploads/types";
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { publicAccess } from "../accesses/publicAccess"; import { publicAccess } from "src/accesses/publicAccess";
import { BuildCollectionConfig, buildCollectionConfig } from "./collectionConfig"; import { CollectionGroups } from "src/shared/payload/constants";
import { CollectionGroups } from "../shared/payload/constants"; import { BuildCollectionConfig, buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { const fields = {
filename: "filename", filename: "filename",

View File

@ -1,6 +1,6 @@
import payload, { GeneratedTypes } from "payload"; import payload, { GeneratedTypes } from "payload";
import { StrapiImage } from "../types/strapi"; import { StrapiImage } from "src/types/strapi";
import { isDefined } from "./asserts"; import { isDefined } from "src/utils/asserts";
type UploadStrapiImage = { type UploadStrapiImage = {
image: StrapiImage; image: StrapiImage;

View File

@ -1,12 +1,12 @@
import tags from "language-tags"; import tags from "language-tags";
import { isUndefined } from "./asserts"; import { RichTextContent } from "src/shared/payload/rich-text";
import { RichTextContent } from "../shared/payload/rich-text"; import { isUndefined } from "src/utils/asserts";
export const shortenEllipsis = (text: string, length: number): string => export const shortenEllipsis = (text: string, length: number): string =>
text.length - 3 > length ? `${text.substring(0, length)}...` : text; text.length - 3 > length ? `${text.substring(0, length)}...` : text;
export const formatLanguageCode = (code: string): string => export const formatLanguageCode = (code: string): string =>
tags(code).valid() ? (tags(code).language()?.descriptions()[0] ?? code) : code; tags(code).valid() ? tags(code).language()?.descriptions()[0] ?? code : code;
export const capitalize = (string: string): string => { export const capitalize = (string: string): string => {
const [firstLetter, ...otherLetters] = string; const [firstLetter, ...otherLetters] = string;

View File

@ -1,6 +1,6 @@
import { CollectionBeforeChangeHook, CollectionConfig, RelationshipField } from "payload/types"; import { CollectionBeforeChangeHook, CollectionConfig, RelationshipField } from "payload/types";
import { BuildCollectionConfig, buildCollectionConfig } from "./collectionConfig"; import { Collections } from "src/shared/payload/constants";
import { Collections } from "../shared/payload/constants"; import { BuildCollectionConfig, buildCollectionConfig } from "src/utils/collectionConfig";
const fields = { updatedBy: "updatedBy" }; const fields = { updatedBy: "updatedBy" };

View File

@ -32,7 +32,7 @@
"module": "commonjs" /* Specify what module code is generated. */, "module": "commonjs" /* Specify what module code is generated. */,
"rootDir": "./src" /* Specify the root folder within your source files. */, "rootDir": "./src" /* Specify the root folder within your source files. */,
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ "baseUrl": "." /* Specify the base directory to resolve non-relative module names. */,
"paths": { "paths": {
"payload/generated-types": ["./src/payload-types.ts"] "payload/generated-types": ["./src/payload-types.ts"]
} /* Specify a set of entries that re-map imports to additional lookup locations. */, } /* Specify a set of entries that re-map imports to additional lookup locations. */,
@ -116,6 +116,7 @@
"exclude": ["node_modules", "dist", "build"], "exclude": ["node_modules", "dist", "build"],
"ts-node": { "ts-node": {
"transpileOnly": true, "transpileOnly": true,
"swc": true "swc": true,
"require": ["tsconfig-paths/register"]
} }
} }