Setup webhooks
This commit is contained in:
parent
5acaf65ade
commit
8ab2e8088a
|
@ -9058,7 +9058,7 @@
|
||||||
},
|
},
|
||||||
"node_modules/payloadcms-relationships": {
|
"node_modules/payloadcms-relationships": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "git+ssh://git@github.com/DrMint/payloadcms-relationships.git#aa65c94f14fa36abe1b482a56fd82d4df3cbfb3e",
|
"resolved": "git+ssh://git@github.com/DrMint/payloadcms-relationships.git#d71e75b32936aac38201ae8740e9336327500476",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"payload": "^2.24.0"
|
"payload": "^2.24.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { imageField } from "../../fields/imageField/imageField";
|
||||||
import { rowField } from "../../fields/rowField/rowField";
|
import { rowField } from "../../fields/rowField/rowField";
|
||||||
import { getConfigEndpoint } from "./endpoints/getConfigEndpoint";
|
import { getConfigEndpoint } from "./endpoints/getConfigEndpoint";
|
||||||
import { Collections, CollectionGroups } from "../../shared/payload/constants";
|
import { Collections, CollectionGroups } from "../../shared/payload/constants";
|
||||||
|
import { globalAfterChangeSendChangesWebhook } from "../../hooks/afterOperationSendChangesWebhook";
|
||||||
|
|
||||||
const fields = {
|
const fields = {
|
||||||
homeBackgroundImage: "homeBackgroundImage",
|
homeBackgroundImage: "homeBackgroundImage",
|
||||||
|
@ -30,6 +31,9 @@ export const WebsiteConfig: GlobalConfig = {
|
||||||
},
|
},
|
||||||
access: { update: mustBeAdmin, read: mustBeAdmin },
|
access: { update: mustBeAdmin, read: mustBeAdmin },
|
||||||
endpoints: [getConfigEndpoint],
|
endpoints: [getConfigEndpoint],
|
||||||
|
hooks: {
|
||||||
|
afterChange: [globalAfterChangeSendChangesWebhook],
|
||||||
|
},
|
||||||
fields: [
|
fields: [
|
||||||
rowField([
|
rowField([
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,383 @@
|
||||||
|
import { Collections } from "../shared/payload/constants";
|
||||||
|
import { getSDKEndpoint } from "../shared/payload/sdk";
|
||||||
|
import { EndpointChange } from "../shared/payload/webhooks";
|
||||||
|
import {
|
||||||
|
Audio,
|
||||||
|
ChronologyEvent,
|
||||||
|
Collectible,
|
||||||
|
Currency,
|
||||||
|
File,
|
||||||
|
Folder,
|
||||||
|
Image,
|
||||||
|
Language,
|
||||||
|
Page,
|
||||||
|
Recorder,
|
||||||
|
Relationship,
|
||||||
|
Video,
|
||||||
|
Wording,
|
||||||
|
} from "../types/collections";
|
||||||
|
import { isPayloadType } from "../utils/asserts";
|
||||||
|
import { AfterChangeHook, AfterDeleteHook } 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";
|
||||||
|
import { RelationshipRemoved } from "payloadcms-relationships";
|
||||||
|
|
||||||
|
export const afterOutgoingRelationRemovedSendChangesWebhook = async ({
|
||||||
|
removedOutgoingRelations,
|
||||||
|
}: RelationshipRemoved) => {
|
||||||
|
const changes: EndpointChange[] = [];
|
||||||
|
|
||||||
|
removedOutgoingRelations?.forEach((relation) =>
|
||||||
|
changes.push(...getEndpointChangesFromOutgoingRelation(relation))
|
||||||
|
);
|
||||||
|
|
||||||
|
await sendWebhookMessage(uniqueBy(changes, ({ url }) => url));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const afterChangeSendChangesWebhook: AfterChangeHook = async ({ doc, collection }) => {
|
||||||
|
if ("_status" in doc && doc._status === "draft") return doc;
|
||||||
|
await commonLogic(collection.slug as keyof GeneratedTypes["collections"], doc);
|
||||||
|
return doc;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const afterDeleteSendChangesWebhook: AfterDeleteHook = async ({ doc, collection }) => {
|
||||||
|
await commonLogic(collection.slug as keyof GeneratedTypes["collections"], doc);
|
||||||
|
return doc;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const globalAfterChangeSendChangesWebhook: GlobalAfterChangeHook = async ({
|
||||||
|
doc,
|
||||||
|
global,
|
||||||
|
}) => {
|
||||||
|
const changes: EndpointChange[] = [];
|
||||||
|
|
||||||
|
switch (global.slug as keyof GeneratedTypes["globals"]) {
|
||||||
|
case Collections.WebsiteConfig:
|
||||||
|
changes.push({ type: "getConfig", url: getSDKEndpoint.getConfigEndpoint() });
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await sendWebhookMessage(uniqueBy(changes, ({ url }) => url));
|
||||||
|
return doc;
|
||||||
|
};
|
||||||
|
|
||||||
|
const commonLogic = async (slug: keyof GeneratedTypes["collections"], doc: any) => {
|
||||||
|
if (slug === "relationships") return doc;
|
||||||
|
if (slug === "payload-migrations") return doc;
|
||||||
|
if (slug === "payload-preferences") return doc;
|
||||||
|
|
||||||
|
let relation: Relationship;
|
||||||
|
try {
|
||||||
|
relation = await findRelationByID(slug, doc.id);
|
||||||
|
} catch (e) {
|
||||||
|
relation = {
|
||||||
|
id: doc.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))
|
||||||
|
);
|
||||||
|
|
||||||
|
await sendWebhookMessage(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(value);
|
||||||
|
|
||||||
|
case Collections.Currencies:
|
||||||
|
return getEndpointChangesForCurrency(value);
|
||||||
|
|
||||||
|
case Collections.Wordings:
|
||||||
|
return getEndpointChangesForWording(value);
|
||||||
|
|
||||||
|
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 [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEndpointChangesForFolder = ({ slug }: Folder): EndpointChange[] => [
|
||||||
|
{ type: "getFolder", slug, url: getSDKEndpoint.getFolderEndpoint(slug) },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForLanguage = (_: Language): EndpointChange[] => [
|
||||||
|
{ type: "getLanguages", url: getSDKEndpoint.getLanguagesEndpoint() },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForCurrency = (_: Currency): EndpointChange[] => [
|
||||||
|
{ type: "getCurrencies", url: getSDKEndpoint.getCurrenciesEndpoint() },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForWording = (_: Wording): EndpointChange[] => [
|
||||||
|
{ type: "getWordings", url: getSDKEndpoint.getWordingsEndpoint() },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForPage = ({ slug }: Page): EndpointChange[] => [
|
||||||
|
{ type: "getPage", slug, url: getSDKEndpoint.getPageEndpoint(slug) },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForCollectible = ({
|
||||||
|
slug,
|
||||||
|
gallery,
|
||||||
|
scans,
|
||||||
|
scansEnabled,
|
||||||
|
}: Collectible): EndpointChange[] => {
|
||||||
|
const changes: EndpointChange[] = [];
|
||||||
|
|
||||||
|
if (gallery && gallery.length > 0) {
|
||||||
|
changes.push({
|
||||||
|
type: "getCollectibleGallery",
|
||||||
|
slug,
|
||||||
|
url: getSDKEndpoint.getCollectibleGalleryEndpoint(slug),
|
||||||
|
});
|
||||||
|
gallery.forEach((_, indexNumber) => {
|
||||||
|
const index = indexNumber.toString();
|
||||||
|
changes.push({
|
||||||
|
type: "getCollectibleGalleryImage",
|
||||||
|
slug,
|
||||||
|
index: index,
|
||||||
|
url: getSDKEndpoint.getCollectibleGalleryImageEndpoint(slug, index),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scans && scansEnabled) {
|
||||||
|
changes.push({
|
||||||
|
type: "getCollectibleScans",
|
||||||
|
slug,
|
||||||
|
url: getSDKEndpoint.getCollectibleScansEndpoint(slug),
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Add other changes for cover, obi, dustjacket...
|
||||||
|
|
||||||
|
scans.pages?.forEach((_, indexNumber) => {
|
||||||
|
const index = indexNumber.toString();
|
||||||
|
changes.push({
|
||||||
|
type: "getCollectibleScanPage",
|
||||||
|
slug,
|
||||||
|
index: index,
|
||||||
|
url: getSDKEndpoint.getCollectibleScanPageEndpoint(slug, index),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEndpointChangesForAudio = ({ id }: Audio): EndpointChange[] => [
|
||||||
|
{ type: "getAudioByID", id, url: getSDKEndpoint.getAudioByIDEndpoint(id) },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForImage = ({ id }: Image): EndpointChange[] => [
|
||||||
|
{ type: "getImageByID", id, url: getSDKEndpoint.getImageByIDEndpoint(id) },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForVideo = ({ id }: Video): EndpointChange[] => [
|
||||||
|
{ type: "getVideoByID", id, url: getSDKEndpoint.getVideoByIDEndpoint(id) },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForFile = ({ id }: File): EndpointChange[] => [
|
||||||
|
{ type: "getFileByID", id, url: getSDKEndpoint.getFileByIDEndpoint(id) },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForRecorder = ({ id }: Recorder): EndpointChange[] => [
|
||||||
|
{ type: "getRecorderByID", id, url: getSDKEndpoint.getRecorderByIDEndpoint(id) },
|
||||||
|
];
|
||||||
|
|
||||||
|
const getEndpointChangesForChronologyEvent = ({ id }: ChronologyEvent): EndpointChange[] => [
|
||||||
|
{
|
||||||
|
type: "getChronologyEventByID",
|
||||||
|
id,
|
||||||
|
url: getSDKEndpoint.getChronologyEventByIDEndpoint(id),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "getChronologyEvents",
|
||||||
|
url: getSDKEndpoint.getChronologyEventsEndpoint(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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) {
|
||||||
|
console.warn("Error while sending webhook", e);
|
||||||
|
}
|
||||||
|
};
|
|
@ -34,6 +34,7 @@ import { Collections } from "./shared/payload/constants";
|
||||||
import { relationshipsPlugin } from "payloadcms-relationships";
|
import { relationshipsPlugin } from "payloadcms-relationships";
|
||||||
import { shownOnlyToAdmin } from "./accesses/collections/shownOnlyToAdmin";
|
import { shownOnlyToAdmin } from "./accesses/collections/shownOnlyToAdmin";
|
||||||
import { mustBeAdmin } from "./accesses/fields/mustBeAdmin";
|
import { mustBeAdmin } from "./accesses/fields/mustBeAdmin";
|
||||||
|
import { afterOutgoingRelationRemovedSendChangesWebhook } from "./hooks/afterOperationSendChangesWebhook";
|
||||||
|
|
||||||
const configuredSftpAdapter = sftpAdapter({
|
const configuredSftpAdapter = sftpAdapter({
|
||||||
connectOptions: {
|
connectOptions: {
|
||||||
|
@ -100,6 +101,7 @@ export default buildConfig({
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
relationshipsPlugin({
|
relationshipsPlugin({
|
||||||
|
// rebuildOnInit: true,
|
||||||
collectionConfig: {
|
collectionConfig: {
|
||||||
admin: {
|
admin: {
|
||||||
hidden: shownOnlyToAdmin,
|
hidden: shownOnlyToAdmin,
|
||||||
|
@ -110,6 +112,7 @@ export default buildConfig({
|
||||||
delete: mustBeAdmin,
|
delete: mustBeAdmin,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
onOutgoingRelationRemoved: afterOutgoingRelationRemovedSendChangesWebhook,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
cloudStorage({
|
cloudStorage({
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7d6f5ffb704f4ecddfb0e0982ce9eb79d39d450d
|
Subproject commit 3582a48bd12a66e99121d9fbc01681971a7d943a
|
|
@ -1248,88 +1248,90 @@ export interface Relationship {
|
||||||
}
|
}
|
||||||
)[]
|
)[]
|
||||||
| null;
|
| null;
|
||||||
outgoingRelations: (
|
outgoingRelations?:
|
||||||
| {
|
| (
|
||||||
relationTo: "pages";
|
| {
|
||||||
value: string | Page;
|
relationTo: "pages";
|
||||||
}
|
value: string | Page;
|
||||||
| {
|
}
|
||||||
relationTo: "collectibles";
|
| {
|
||||||
value: string | Collectible;
|
relationTo: "collectibles";
|
||||||
}
|
value: string | Collectible;
|
||||||
| {
|
}
|
||||||
relationTo: "folders";
|
| {
|
||||||
value: string | Folder;
|
relationTo: "folders";
|
||||||
}
|
value: string | Folder;
|
||||||
| {
|
}
|
||||||
relationTo: "chronology-events";
|
| {
|
||||||
value: string | ChronologyEvent;
|
relationTo: "chronology-events";
|
||||||
}
|
value: string | ChronologyEvent;
|
||||||
| {
|
}
|
||||||
relationTo: "images";
|
| {
|
||||||
value: string | Image;
|
relationTo: "images";
|
||||||
}
|
value: string | Image;
|
||||||
| {
|
}
|
||||||
relationTo: "audios";
|
| {
|
||||||
value: string | Audio;
|
relationTo: "audios";
|
||||||
}
|
value: string | Audio;
|
||||||
| {
|
}
|
||||||
relationTo: "media-thumbnails";
|
| {
|
||||||
value: string | MediaThumbnail;
|
relationTo: "media-thumbnails";
|
||||||
}
|
value: string | MediaThumbnail;
|
||||||
| {
|
}
|
||||||
relationTo: "videos";
|
| {
|
||||||
value: string | Video;
|
relationTo: "videos";
|
||||||
}
|
value: string | Video;
|
||||||
| {
|
}
|
||||||
relationTo: "videos-subtitles";
|
| {
|
||||||
value: string | VideoSubtitle;
|
relationTo: "videos-subtitles";
|
||||||
}
|
value: string | VideoSubtitle;
|
||||||
| {
|
}
|
||||||
relationTo: "videos-channels";
|
| {
|
||||||
value: string | VideosChannel;
|
relationTo: "videos-channels";
|
||||||
}
|
value: string | VideosChannel;
|
||||||
| {
|
}
|
||||||
relationTo: "files";
|
| {
|
||||||
value: string | File;
|
relationTo: "files";
|
||||||
}
|
value: string | File;
|
||||||
| {
|
}
|
||||||
relationTo: "scans";
|
| {
|
||||||
value: string | Scan;
|
relationTo: "scans";
|
||||||
}
|
value: string | Scan;
|
||||||
| {
|
}
|
||||||
relationTo: "tags";
|
| {
|
||||||
value: string | Tag;
|
relationTo: "tags";
|
||||||
}
|
value: string | Tag;
|
||||||
| {
|
}
|
||||||
relationTo: "attributes";
|
| {
|
||||||
value: string | Attribute;
|
relationTo: "attributes";
|
||||||
}
|
value: string | Attribute;
|
||||||
| {
|
}
|
||||||
relationTo: "credits-roles";
|
| {
|
||||||
value: string | CreditsRole;
|
relationTo: "credits-roles";
|
||||||
}
|
value: string | CreditsRole;
|
||||||
| {
|
}
|
||||||
relationTo: "recorders";
|
| {
|
||||||
value: string | Recorder;
|
relationTo: "recorders";
|
||||||
}
|
value: string | Recorder;
|
||||||
| {
|
}
|
||||||
relationTo: "languages";
|
| {
|
||||||
value: string | Language;
|
relationTo: "languages";
|
||||||
}
|
value: string | Language;
|
||||||
| {
|
}
|
||||||
relationTo: "currencies";
|
| {
|
||||||
value: string | Currency;
|
relationTo: "currencies";
|
||||||
}
|
value: string | Currency;
|
||||||
| {
|
}
|
||||||
relationTo: "wordings";
|
| {
|
||||||
value: string | Wording;
|
relationTo: "wordings";
|
||||||
}
|
value: string | Wording;
|
||||||
| {
|
}
|
||||||
relationTo: "generic-contents";
|
| {
|
||||||
value: string | GenericContent;
|
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
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,6 +1,10 @@
|
||||||
import { GeneratedTypes } from "payload";
|
import { GeneratedTypes } from "payload";
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
import { formatToPascalCase } from "./string";
|
import { formatToPascalCase } from "./string";
|
||||||
|
import {
|
||||||
|
afterChangeSendChangesWebhook,
|
||||||
|
afterDeleteSendChangesWebhook,
|
||||||
|
} from "../hooks/afterOperationSendChangesWebhook";
|
||||||
|
|
||||||
type CollectionConfigWithPlugins = CollectionConfig;
|
type CollectionConfigWithPlugins = CollectionConfig;
|
||||||
|
|
||||||
|
@ -15,4 +19,9 @@ export type BuildCollectionConfig = Omit<
|
||||||
export const buildCollectionConfig = (config: BuildCollectionConfig): CollectionConfig => ({
|
export const buildCollectionConfig = (config: BuildCollectionConfig): CollectionConfig => ({
|
||||||
...config,
|
...config,
|
||||||
typescript: { interface: formatToPascalCase(config.labels.singular) },
|
typescript: { interface: formatToPascalCase(config.labels.singular) },
|
||||||
|
hooks: {
|
||||||
|
...config.hooks,
|
||||||
|
afterChange: [...(config.hooks?.afterChange ?? []), afterChangeSendChangesWebhook],
|
||||||
|
afterDelete: [...(config.hooks?.afterDelete ?? []), afterDeleteSendChangesWebhook],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue