Files
payload/src/collections/operations/create.js

235 lines
5.9 KiB
JavaScript

const mkdirp = require('mkdirp');
const path = require('path');
const crypto = require('crypto');
const executeAccess = require('../../auth/executeAccess');
const removeInternalFields = require('../../utilities/removeInternalFields');
const { MissingFile, FileUploadError } = require('../../errors');
const resizeAndSave = require('../../uploads/imageResizer');
const getSafeFilename = require('../../uploads/getSafeFilename');
const getImageSize = require('../../uploads/getImageSize');
const imageMIMETypes = require('../../uploads/imageMIMETypes');
const sendVerificationEmail = require('../../auth/sendVerificationEmail');
async function create(args) {
const { performFieldOperations, config } = this;
const {
collection: {
Model,
config: collectionConfig,
},
req,
req: {
locale,
fallbackLocale,
},
disableVerificationEmail,
depth,
overrideAccess,
showHiddenFields,
} = args;
let { data } = args;
// /////////////////////////////////////
// 1. Retrieve and execute access
// /////////////////////////////////////
if (!overrideAccess) {
await executeAccess({ req }, collectionConfig.access.create);
}
// /////////////////////////////////////
// 2. Execute beforeValidate field-level hooks, access, and validation
// /////////////////////////////////////
data = await this.performFieldOperations(collectionConfig, {
data,
req,
hook: 'beforeValidate',
operation: 'create',
overrideAccess,
});
// /////////////////////////////////////
// 3. Execute beforeValidate collection hooks
// /////////////////////////////////////
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
await priorHook;
data = (await hook({
data,
req,
operation: 'create',
})) || data;
}, Promise.resolve());
// /////////////////////////////////////
// 4. Execute field-level access, beforeChange hooks, and validation
// /////////////////////////////////////
data = await performFieldOperations(collectionConfig, {
data,
hook: 'beforeChange',
operation: 'create',
req,
overrideAccess,
});
// /////////////////////////////////////
// 5. Execute beforeChange collection hooks
// /////////////////////////////////////
await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
await priorHook;
data = (await hook({
data,
req,
operation: 'create',
})) || data;
}, Promise.resolve());
// /////////////////////////////////////
// 6. Upload and resize any files that may be present
// /////////////////////////////////////
if (collectionConfig.upload) {
const fileData = {};
const { staticDir, imageSizes } = collectionConfig.upload;
const file = (req.files && req.files.file) ? req.files.file : req.file;
if (!file) {
throw new MissingFile();
}
let staticPath = staticDir;
if (staticDir.indexOf('/') !== 0) {
staticPath = path.join(config.paths.configDir, staticDir);
}
mkdirp.sync(staticPath);
const fsSafeName = await getSafeFilename(staticPath, file.name);
try {
await file.mv(`${staticPath}/${fsSafeName}`);
if (imageMIMETypes.indexOf(file.mimetype) > -1) {
const dimensions = await getImageSize(`${staticPath}/${fsSafeName}`);
fileData.width = dimensions.width;
fileData.height = dimensions.height;
if (Array.isArray(imageSizes) && file.mimetype !== 'image/svg+xml') {
fileData.sizes = await resizeAndSave(staticPath, collectionConfig, fsSafeName, fileData.mimeType);
}
}
} catch (err) {
console.error(err);
throw new FileUploadError(err);
}
fileData.filename = fsSafeName;
fileData.filesize = file.size;
fileData.mimeType = file.mimetype;
data = {
...data,
...fileData,
};
}
// /////////////////////////////////////
// 7. Perform database operation
// /////////////////////////////////////
let result = new Model();
if (locale && result.setLocale) {
result.setLocale(locale, fallbackLocale);
}
if (collectionConfig.auth) {
if (data.email) {
data.email = data.email.toLowerCase();
}
if (collectionConfig.auth.verify) {
data._verified = false;
data._verificationToken = crypto.randomBytes(20).toString('hex');
}
}
Object.assign(result, data);
if (collectionConfig.auth) {
result = await Model.register(result, data.password);
} else {
await result.save();
}
result = result.toJSON({ virtuals: true });
// /////////////////////////////////////
// 8. Execute field-level hooks and access
// /////////////////////////////////////
result = await performFieldOperations(collectionConfig, {
data: result,
hook: 'afterRead',
operation: 'read',
req,
depth,
overrideAccess,
showHiddenFields,
});
// /////////////////////////////////////
// 9. Execute after collection hook
// /////////////////////////////////////
await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
await priorHook;
result = await hook({
doc: result,
req: args.req,
operation: 'create',
}) || result;
}, Promise.resolve());
// /////////////////////////////////////
// 10. Send verification email if applicable
// /////////////////////////////////////
if (collectionConfig.auth && collectionConfig.auth.verify && !disableVerificationEmail) {
sendVerificationEmail({
config: this.config,
sendEmail: this.sendEmail,
collection: { config: collectionConfig, Model },
user: result,
token: data._verificationToken,
req,
});
}
// /////////////////////////////////////
// 11. Return results
// /////////////////////////////////////
result = removeInternalFields(result);
result = JSON.stringify(result);
result = JSON.parse(result);
return result;
}
module.exports = create;