feat: initial drafts and versions merge
This commit is contained in:
@@ -12,7 +12,7 @@ type Arguments = {
|
||||
operation: Operation
|
||||
overrideAccess: boolean
|
||||
req: PayloadRequest
|
||||
id: string
|
||||
id: string | number
|
||||
relationshipPopulations: (() => Promise<void>)[]
|
||||
depth: number
|
||||
currentDepth: number
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import crypto from 'crypto';
|
||||
import { Field, FieldHook } from '../config/types';
|
||||
|
||||
const encryptKey: FieldHook = ({ req, value }) => (value ? req.payload.encrypt(value as string) : undefined);
|
||||
const decryptKey: FieldHook = ({ req, value }) => (value ? req.payload.decrypt(value as string) : undefined);
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'enableAPIKey',
|
||||
label: 'Enable API Key',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
admin: {
|
||||
components: {
|
||||
Field: () => null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'apiKey',
|
||||
label: 'API Key',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: () => null,
|
||||
},
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
encryptKey,
|
||||
],
|
||||
afterRead: [
|
||||
decryptKey,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'apiKeyIndex',
|
||||
type: 'text',
|
||||
hidden: true,
|
||||
admin: {
|
||||
disabled: true,
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
async ({ data, req, value }) => {
|
||||
if (data.apiKey) {
|
||||
return crypto.createHmac('sha1', req.payload.secret)
|
||||
.update(data.apiKey as string)
|
||||
.digest('hex');
|
||||
}
|
||||
if (data.enableAPIKey === false) {
|
||||
return null;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
] as Field[];
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Field } from '../config/types';
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'loginAttempts',
|
||||
type: 'number',
|
||||
hidden: true,
|
||||
defaultValue: 0,
|
||||
},
|
||||
{
|
||||
name: 'lockUntil',
|
||||
type: 'date',
|
||||
hidden: true,
|
||||
},
|
||||
] as Field[];
|
||||
@@ -1,27 +0,0 @@
|
||||
import { email } from '../validations';
|
||||
import { Field } from '../config/types';
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
type: 'email',
|
||||
validate: email,
|
||||
unique: true,
|
||||
admin: {
|
||||
components: {
|
||||
Field: () => null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'resetPasswordToken',
|
||||
type: 'text',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
name: 'resetPasswordExpiration',
|
||||
type: 'date',
|
||||
hidden: true,
|
||||
},
|
||||
] as Field[];
|
||||
@@ -1,43 +0,0 @@
|
||||
import { Field, FieldHook } from '../config/types';
|
||||
|
||||
const autoRemoveVerificationToken: FieldHook = ({ originalDoc, data, value, operation }) => {
|
||||
// If a user manually sets `_verified` to true,
|
||||
// and it was `false`, set _verificationToken to `null`.
|
||||
// This is useful because the admin panel
|
||||
// allows users to set `_verified` to true manually
|
||||
|
||||
if (operation === 'update') {
|
||||
if (data?._verified === true && originalDoc?._verified === false) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
name: '_verified',
|
||||
type: 'checkbox',
|
||||
access: {
|
||||
create: () => false,
|
||||
update: ({ req: { user } }) => Boolean(user),
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
},
|
||||
admin: {
|
||||
components: {
|
||||
Field: () => null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '_verificationToken',
|
||||
type: 'text',
|
||||
hidden: true,
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
autoRemoveVerificationToken,
|
||||
],
|
||||
},
|
||||
},
|
||||
] as Field[];
|
||||
@@ -1,149 +0,0 @@
|
||||
import { Field } from '../config/types';
|
||||
import { Config } from '../../config/types';
|
||||
import { CollectionConfig } from '../../collections/config/types';
|
||||
import { mimeTypeValidator } from '../../uploads/mimeTypeValidator';
|
||||
import { IncomingUploadType } from '../../uploads/types';
|
||||
|
||||
type Options = {
|
||||
config: Config
|
||||
collection: CollectionConfig
|
||||
}
|
||||
|
||||
const getBaseUploadFields = ({ config, collection }: Options): Field[] => {
|
||||
const uploadOptions: IncomingUploadType = typeof collection.upload === 'object' ? collection.upload : {};
|
||||
|
||||
const mimeType: Field = {
|
||||
name: 'mimeType',
|
||||
label: 'MIME Type',
|
||||
type: 'text',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
const url: Field = {
|
||||
name: 'url',
|
||||
label: 'URL',
|
||||
type: 'text',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
const width: Field = {
|
||||
name: 'width',
|
||||
label: 'Width',
|
||||
type: 'number',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
const height: Field = {
|
||||
name: 'height',
|
||||
label: 'Height',
|
||||
type: 'number',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
const filesize: Field = {
|
||||
name: 'filesize',
|
||||
label: 'File Size',
|
||||
type: 'number',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
const filename: Field = {
|
||||
name: 'filename',
|
||||
label: 'File Name',
|
||||
type: 'text',
|
||||
index: true,
|
||||
unique: true,
|
||||
admin: {
|
||||
readOnly: true,
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
let uploadFields: Field[] = [
|
||||
{
|
||||
...url,
|
||||
hooks: {
|
||||
afterRead: [
|
||||
({ data }) => {
|
||||
if (data?.filename) {
|
||||
return `${config.serverURL}${uploadOptions.staticURL}/${data.filename}`;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
filename,
|
||||
mimeType,
|
||||
filesize,
|
||||
];
|
||||
|
||||
if (uploadOptions.mimeTypes) {
|
||||
mimeType.validate = mimeTypeValidator(uploadOptions.mimeTypes);
|
||||
}
|
||||
|
||||
if (uploadOptions.imageSizes) {
|
||||
uploadFields = uploadFields.concat([
|
||||
width,
|
||||
height,
|
||||
{
|
||||
name: 'sizes',
|
||||
label: 'Sizes',
|
||||
type: 'group',
|
||||
admin: {
|
||||
disabled: true,
|
||||
},
|
||||
fields: uploadOptions.imageSizes.map((size) => ({
|
||||
label: size.name,
|
||||
name: size.name,
|
||||
type: 'group',
|
||||
admin: {
|
||||
disabled: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
...url,
|
||||
hooks: {
|
||||
afterRead: [
|
||||
({ data }) => {
|
||||
const sizeFilename = data?.sizes?.[size.name]?.filename;
|
||||
|
||||
if (sizeFilename) {
|
||||
return `${config.serverURL}${uploadOptions.staticURL}/${sizeFilename}`;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
width,
|
||||
height,
|
||||
mimeType,
|
||||
filesize,
|
||||
filename,
|
||||
],
|
||||
})),
|
||||
},
|
||||
]);
|
||||
}
|
||||
return uploadFields;
|
||||
};
|
||||
|
||||
export default getBaseUploadFields;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PayloadRequest } from '../express/types';
|
||||
import { Operation } from '../types';
|
||||
import { HookName, FieldAffectingData } from './config/types';
|
||||
import { HookName, FieldAffectingData, FieldHook } from './config/types';
|
||||
|
||||
type Arguments = {
|
||||
data: Record<string, unknown>
|
||||
@@ -10,37 +10,80 @@ type Arguments = {
|
||||
operation: Operation
|
||||
fullOriginalDoc: Record<string, unknown>
|
||||
fullData: Record<string, unknown>
|
||||
flattenLocales: boolean
|
||||
isVersion: boolean
|
||||
}
|
||||
|
||||
const hookPromise = async ({
|
||||
data,
|
||||
field,
|
||||
hook,
|
||||
req,
|
||||
operation,
|
||||
type ExecuteHookArguments = {
|
||||
currentHook: FieldHook
|
||||
value: unknown
|
||||
} & Arguments;
|
||||
|
||||
const executeHook = async ({
|
||||
currentHook,
|
||||
fullOriginalDoc,
|
||||
fullData,
|
||||
}: Arguments): Promise<void> => {
|
||||
const resultingData = data;
|
||||
operation,
|
||||
req,
|
||||
value,
|
||||
}: ExecuteHookArguments) => {
|
||||
let hookedValue = await currentHook({
|
||||
value,
|
||||
originalDoc: fullOriginalDoc,
|
||||
data: fullData,
|
||||
operation,
|
||||
req,
|
||||
});
|
||||
|
||||
if (typeof hookedValue === 'undefined') {
|
||||
hookedValue = value;
|
||||
}
|
||||
|
||||
return hookedValue;
|
||||
};
|
||||
|
||||
const hookPromise = async (args: Arguments): Promise<void> => {
|
||||
const {
|
||||
field,
|
||||
hook,
|
||||
req,
|
||||
flattenLocales,
|
||||
data,
|
||||
} = args;
|
||||
|
||||
if (field.hooks && field.hooks[hook]) {
|
||||
await field.hooks[hook].reduce(async (priorHook, currentHook) => {
|
||||
await priorHook;
|
||||
|
||||
let hookedValue = await currentHook({
|
||||
value: data[field.name],
|
||||
originalDoc: fullOriginalDoc,
|
||||
data: fullData,
|
||||
operation,
|
||||
req,
|
||||
});
|
||||
const shouldRunHookOnAllLocales = hook === 'afterRead'
|
||||
&& field.localized
|
||||
&& (req.locale === 'all' || !flattenLocales)
|
||||
&& typeof data[field.name] === 'object';
|
||||
|
||||
if (typeof hookedValue === 'undefined') {
|
||||
hookedValue = data[field.name];
|
||||
}
|
||||
if (shouldRunHookOnAllLocales) {
|
||||
const hookPromises = Object.entries(data[field.name]).map(([locale, value]) => (async () => {
|
||||
const hookedValue = await executeHook({
|
||||
...args,
|
||||
currentHook,
|
||||
value,
|
||||
});
|
||||
|
||||
if (hookedValue !== undefined) {
|
||||
resultingData[field.name] = hookedValue;
|
||||
if (hookedValue !== undefined) {
|
||||
data[field.name][locale] = hookedValue;
|
||||
}
|
||||
})());
|
||||
|
||||
await Promise.all(hookPromises);
|
||||
} else {
|
||||
const hookedValue = await executeHook({
|
||||
...args,
|
||||
value: data[field.name],
|
||||
currentHook,
|
||||
});
|
||||
|
||||
if (hookedValue !== undefined) {
|
||||
data[field.name] = hookedValue;
|
||||
}
|
||||
}
|
||||
}, Promise.resolve());
|
||||
}
|
||||
|
||||
@@ -19,13 +19,15 @@ type Arguments = {
|
||||
unflattenLocales?: boolean
|
||||
originalDoc?: Record<string, unknown>
|
||||
docWithLocales?: Record<string, unknown>
|
||||
id?: string
|
||||
id?: string | number
|
||||
showHiddenFields?: boolean
|
||||
depth?: number
|
||||
currentDepth?: number
|
||||
isVersion?: boolean
|
||||
skipValidation?: boolean
|
||||
}
|
||||
|
||||
export default async function performFieldOperations(this: Payload, entityConfig: SanitizedCollectionConfig | SanitizedGlobalConfig, args: Arguments): Promise<{ [key: string]: unknown }> {
|
||||
export default async function performFieldOperations(this: Payload, entityConfig: SanitizedCollectionConfig | SanitizedGlobalConfig, args: Arguments): Promise<any> {
|
||||
const {
|
||||
data,
|
||||
originalDoc: fullOriginalDoc,
|
||||
@@ -42,6 +44,8 @@ export default async function performFieldOperations(this: Payload, entityConfig
|
||||
flattenLocales,
|
||||
unflattenLocales = false,
|
||||
showHiddenFields = false,
|
||||
isVersion = false,
|
||||
skipValidation = false,
|
||||
} = args;
|
||||
|
||||
const fullData = deepCopyObject(data);
|
||||
@@ -101,6 +105,8 @@ export default async function performFieldOperations(this: Payload, entityConfig
|
||||
unflattenLocaleActions,
|
||||
transformActions,
|
||||
docWithLocales,
|
||||
isVersion,
|
||||
skipValidation,
|
||||
});
|
||||
|
||||
if (hook === 'afterRead') {
|
||||
|
||||
@@ -42,10 +42,10 @@ const populate = async ({
|
||||
let populatedRelationship;
|
||||
|
||||
if (depth && currentDepth <= depth) {
|
||||
populatedRelationship = await payload.operations.collections.findByID({
|
||||
populatedRelationship = await payload.findByID({
|
||||
req,
|
||||
collection: relatedCollection,
|
||||
id: idString,
|
||||
collection: relatedCollection.config.slug,
|
||||
id: idString as string,
|
||||
currentDepth: currentDepth + 1,
|
||||
overrideAccess,
|
||||
disableErrors: true,
|
||||
|
||||
@@ -19,7 +19,7 @@ type Arguments = {
|
||||
operation: Operation
|
||||
overrideAccess: boolean
|
||||
req: PayloadRequest
|
||||
id?: string
|
||||
id?: string | number
|
||||
relationshipPopulations: (() => Promise<void>)[]
|
||||
depth: number
|
||||
currentDepth: number
|
||||
@@ -36,6 +36,7 @@ type Arguments = {
|
||||
transformActions: (() => void)[]
|
||||
docWithLocales?: Record<string, any>
|
||||
skipValidation?: boolean
|
||||
isVersion: boolean
|
||||
}
|
||||
|
||||
const traverseFields = (args: Arguments): void => {
|
||||
@@ -68,6 +69,7 @@ const traverseFields = (args: Arguments): void => {
|
||||
transformActions,
|
||||
docWithLocales = {},
|
||||
skipValidation,
|
||||
isVersion,
|
||||
} = args;
|
||||
|
||||
fields.forEach((field) => {
|
||||
@@ -226,6 +228,8 @@ const traverseFields = (args: Arguments): void => {
|
||||
operation,
|
||||
fullOriginalDoc,
|
||||
fullData,
|
||||
flattenLocales,
|
||||
isVersion,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user