| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  | const path = require('path'); | 
					
						
							|  |  |  | const os = require('os'); | 
					
						
							| 
									
										
										
										
											2022-03-25 15:59:42 +01:00
										 |  |  | const mime = require('mime-types'); | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  | const fse = require('fs-extra'); | 
					
						
							| 
									
										
										
										
											2022-05-13 16:10:18 +02:00
										 |  |  | const { FILE_MODEL_UID } = require('./constants'); | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  | const { getStreamSize } = require('./utils/file'); | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | const UPLOAD_MUTATION_NAME = 'upload'; | 
					
						
							|  |  |  | const MULTIPLE_UPLOAD_MUTATION_NAME = 'multipleUpload'; | 
					
						
							|  |  |  | const UPDATE_FILE_INFO_MUTATION_NAME = 'updateFileInfo'; | 
					
						
							|  |  |  | const DELETE_FILE_MUTATION_NAME = 'removeFile'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const FILE_INFO_INPUT_TYPE_NAME = 'FileInfoInput'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-29 10:47:10 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {{ strapi: import('@strapi/strapi').Strapi }} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  | module.exports = ({ strapi }) => { | 
					
						
							| 
									
										
										
										
											2021-09-22 18:17:51 +02:00
										 |  |  |   const { service: getGraphQLService, config: graphQLConfig } = strapi.plugin('graphql'); | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  |   const { service: getUploadService } = strapi.plugin('upload'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 18:17:51 +02:00
										 |  |  |   const isShadowCRUDEnabled = graphQLConfig('shadowCRUD', true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!isShadowCRUDEnabled) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  |   const { getTypeName, getEntityResponseName } = getGraphQLService('utils').naming; | 
					
						
							|  |  |  |   const { toEntityResponse } = getGraphQLService('format').returnTypes; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-13 16:10:18 +02:00
										 |  |  |   const fileModel = strapi.getModel(FILE_MODEL_UID); | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  |   const fileTypeName = getTypeName(fileModel); | 
					
						
							|  |  |  |   const fileEntityResponseType = getEntityResponseName(fileModel); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-28 10:49:16 +01:00
										 |  |  |   const { optimize, isSupportedImage } = getUploadService('image-manipulation'); | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Optimize and format a file using the upload services | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * @param {object} upload | 
					
						
							|  |  |  |    * @param {object} extraInfo | 
					
						
							|  |  |  |    * @param {object} metas | 
					
						
							|  |  |  |    * @return {Promise<object>} | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2022-02-16 12:23:11 +01:00
										 |  |  |   const formatFile = async (upload, extraInfo, metas) => { | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  |     const uploadService = getUploadService('upload'); | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |     const { filename, mimetype, createReadStream } = await upload; | 
					
						
							| 
									
										
										
										
											2022-03-30 16:26:09 +02:00
										 |  |  |     const currentFile = await uploadService.formatFileInfo( | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  |       { | 
					
						
							|  |  |  |         filename, | 
					
						
							| 
									
										
										
										
											2022-05-05 12:29:06 +02:00
										 |  |  |         /** | 
					
						
							|  |  |  |          * in case the mime-type wasn't sent, Strapi tries to guess it | 
					
						
							|  |  |  |          * from the file extension, to avoid a corrupt database state | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         type: mimetype || mime.lookup(filename) || 'application/octet-stream', | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |         size: await getStreamSize(createReadStream()), | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  |       }, | 
					
						
							|  |  |  |       extraInfo || {}, | 
					
						
							|  |  |  |       metas | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |     currentFile.getStream = createReadStream; | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-28 10:49:16 +01:00
										 |  |  |     if (!(await isSupportedImage(currentFile))) { | 
					
						
							|  |  |  |       return currentFile; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return optimize(currentFile); | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Register Upload's types, queries & mutations to the content API using the GraphQL extension API | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  |   getGraphQLService('extension').use(({ nexus }) => { | 
					
						
							|  |  |  |     const { inputObjectType, extendType, nonNull, list } = nexus; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Represents the input data payload for the file's information
 | 
					
						
							|  |  |  |     const fileInfoInputType = inputObjectType({ | 
					
						
							|  |  |  |       name: FILE_INFO_INPUT_TYPE_NAME, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       definition(t) { | 
					
						
							|  |  |  |         t.string('name'); | 
					
						
							|  |  |  |         t.string('alternativeText'); | 
					
						
							|  |  |  |         t.string('caption'); | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const mutations = extendType({ | 
					
						
							|  |  |  |       type: 'Mutation', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       definition(t) { | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * Upload a single file | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         t.field(UPLOAD_MUTATION_NAME, { | 
					
						
							|  |  |  |           type: nonNull(fileEntityResponseType), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           args: { | 
					
						
							|  |  |  |             refId: 'ID', | 
					
						
							|  |  |  |             ref: 'String', | 
					
						
							|  |  |  |             field: 'String', | 
					
						
							|  |  |  |             info: FILE_INFO_INPUT_TYPE_NAME, | 
					
						
							|  |  |  |             file: nonNull('Upload'), | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           async resolve(parent, args) { | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |             // create temporary folder to store files for stream manipulation
 | 
					
						
							| 
									
										
										
										
											2022-02-16 12:23:11 +01:00
										 |  |  |             const tmpWorkingDirectory = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-upload-')); | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |             let sanitizedEntity; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             try { | 
					
						
							| 
									
										
										
										
											2022-05-03 20:33:09 +02:00
										 |  |  |               const { file: upload, info = {}, ...metas } = args; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               const apiUploadFolderService = getUploadService('api-upload-folder'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               const apiUploadFolder = await apiUploadFolderService.getAPIUploadFolder(); | 
					
						
							|  |  |  |               info.folder = apiUploadFolder.id; | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-16 12:23:11 +01:00
										 |  |  |               const file = await formatFile(upload, info, { ...metas, tmpWorkingDirectory }); | 
					
						
							|  |  |  |               const uploadedFile = await getUploadService('upload').uploadFileAndPersist(file, {}); | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |               sanitizedEntity = await toEntityResponse(uploadedFile, { | 
					
						
							|  |  |  |                 args, | 
					
						
							|  |  |  |                 resourceUID: fileTypeName, | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |             } finally { | 
					
						
							|  |  |  |               // delete temporary folder
 | 
					
						
							| 
									
										
										
										
											2022-02-16 12:23:11 +01:00
										 |  |  |               await fse.remove(tmpWorkingDirectory); | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return sanitizedEntity; | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  |           }, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * Upload multiple files | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         t.field(MULTIPLE_UPLOAD_MUTATION_NAME, { | 
					
						
							|  |  |  |           type: nonNull(list(fileEntityResponseType)), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           args: { | 
					
						
							|  |  |  |             refId: 'ID', | 
					
						
							|  |  |  |             ref: 'String', | 
					
						
							|  |  |  |             field: 'String', | 
					
						
							|  |  |  |             files: nonNull(list('Upload')), | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           async resolve(parent, args) { | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |             // create temporary folder to store files for stream manipulation
 | 
					
						
							| 
									
										
										
										
											2022-02-16 12:23:11 +01:00
										 |  |  |             const tmpWorkingDirectory = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-upload-')); | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |             let sanitizedEntities = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             try { | 
					
						
							| 
									
										
										
										
											2022-02-16 12:23:11 +01:00
										 |  |  |               const { files: uploads, ...metas } = args; | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 20:33:09 +02:00
										 |  |  |               const apiUploadFolderService = getUploadService('api-upload-folder'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               const apiUploadFolder = await apiUploadFolderService.getAPIUploadFolder(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |               const files = await Promise.all( | 
					
						
							| 
									
										
										
										
											2022-08-08 23:33:39 +02:00
										 |  |  |                 uploads.map((upload) => | 
					
						
							| 
									
										
										
										
											2022-05-03 20:33:09 +02:00
										 |  |  |                   formatFile( | 
					
						
							|  |  |  |                     upload, | 
					
						
							|  |  |  |                     { folder: apiUploadFolder.id }, | 
					
						
							|  |  |  |                     { ...metas, tmpWorkingDirectory } | 
					
						
							|  |  |  |                   ) | 
					
						
							|  |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |               ); | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |               const uploadService = getUploadService('upload'); | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |               const uploadedFiles = await Promise.all( | 
					
						
							| 
									
										
										
										
											2022-08-08 23:33:39 +02:00
										 |  |  |                 files.map((file) => uploadService.uploadFileAndPersist(file, {})) | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |               ); | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-08 23:33:39 +02:00
										 |  |  |               sanitizedEntities = uploadedFiles.map((file) => | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |                 toEntityResponse(file, { args, resourceUID: fileTypeName }) | 
					
						
							|  |  |  |               ); | 
					
						
							|  |  |  |             } finally { | 
					
						
							|  |  |  |               // delete temporary folder
 | 
					
						
							| 
									
										
										
										
											2022-02-16 12:23:11 +01:00
										 |  |  |               await fse.remove(tmpWorkingDirectory); | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-05 17:36:21 +01:00
										 |  |  |             return sanitizedEntities; | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  |           }, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * Update some information for a given file | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         t.field(UPDATE_FILE_INFO_MUTATION_NAME, { | 
					
						
							|  |  |  |           type: nonNull(fileEntityResponseType), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           args: { | 
					
						
							|  |  |  |             id: nonNull('ID'), | 
					
						
							|  |  |  |             info: FILE_INFO_INPUT_TYPE_NAME, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           async resolve(parent, args) { | 
					
						
							|  |  |  |             const { id, info } = args; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             const updatedFile = await getUploadService('upload').updateFileInfo(id, info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return toEntityResponse(updatedFile, { args, resourceUID: fileTypeName }); | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * Delete & remove a given file | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         t.field(DELETE_FILE_MUTATION_NAME, { | 
					
						
							|  |  |  |           type: fileEntityResponseType, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           args: { | 
					
						
							|  |  |  |             id: nonNull('ID'), | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           async resolve(parent, args) { | 
					
						
							|  |  |  |             const { id } = args; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 11:11:03 +02:00
										 |  |  |             const file = await getUploadService('upload').findOne(id); | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (!file) { | 
					
						
							|  |  |  |               return null; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             const deletedFile = await getUploadService('upload').remove(file); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return toEntityResponse(deletedFile, { args, resourceUID: fileTypeName }); | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 15:47:53 +01:00
										 |  |  |     return { | 
					
						
							|  |  |  |       types: [fileInfoInputType, mutations], | 
					
						
							|  |  |  |       resolversConfig: { | 
					
						
							|  |  |  |         // Use custom scopes for the upload file CRUD operations
 | 
					
						
							| 
									
										
										
										
											2022-08-08 15:50:34 +02:00
										 |  |  |         'Query.uploadFiles': { auth: { scope: 'plugin::upload.content-api.find' } }, | 
					
						
							|  |  |  |         'Query.uploadFile': { auth: { scope: 'plugin::upload.content-api.findOne' } }, | 
					
						
							|  |  |  |         'Mutation.createUploadFile': { auth: { scope: 'plugin::upload.content-api.upload' } }, | 
					
						
							|  |  |  |         'Mutation.updateUploadFile': { auth: { scope: 'plugin::upload.content-api.upload' } }, | 
					
						
							|  |  |  |         'Mutation.deleteUploadFile': { auth: { scope: 'plugin::upload.content-api.destroy' } }, | 
					
						
							| 
									
										
										
										
											2021-11-30 16:27:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         [`Mutation.${UPLOAD_MUTATION_NAME}`]: { | 
					
						
							|  |  |  |           auth: { scope: 'plugin::upload.content-api.upload' }, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         [`Mutation.${MULTIPLE_UPLOAD_MUTATION_NAME}`]: { | 
					
						
							|  |  |  |           auth: { scope: 'plugin::upload.content-api.upload' }, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         [`Mutation.${UPDATE_FILE_INFO_MUTATION_NAME}`]: { | 
					
						
							|  |  |  |           auth: { scope: 'plugin::upload.content-api.upload' }, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         [`Mutation.${DELETE_FILE_MUTATION_NAME}`]: { | 
					
						
							|  |  |  |           auth: { scope: 'plugin::upload.content-api.destroy' }, | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2021-11-04 15:47:53 +01:00
										 |  |  |       }, | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2021-09-15 15:44:42 +02:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2021-09-10 15:54:19 +02:00
										 |  |  | }; |