UNPKG

we-plugin-file

Version:

We.js file plugin with suport to storages

600 lines (535 loc) 16.6 kB
/** * We.js we-pluginfile plugin settings */ const multer = require('multer'), path = require('path'); const imageMimeTypes = [ 'image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/bmp', 'image/x-icon', 'image/tiff', 'image/heic', // apple format 'image/heif', // apple format 'image/vnd.microsoft.icon' // .ico ]; module.exports = function loadPlugin (pp, Plugin) { const plugin = new Plugin(__dirname); const lConfig = plugin.we.config; let styles; plugin.multer = multer; if (lConfig.upload && lConfig.upload.image && lConfig.upload.image.avaibleStyles) { styles = lConfig.upload.image.avaibleStyles; } else { styles = [ 'thumbnail', 'medium', 'large' ]; } // set plugin configs plugin.setConfigs({ defaultUserAvatar: path.resolve(__dirname, 'files/public/avatar.png'), permissions: { 'find_image': { 'title': 'Find image', 'description': 'Find and list images' }, 'crop_image': { 'title': 'Crop image', 'description': 'Crop image and update size data' }, 'upload_image': { 'title': 'Upload image', 'description': 'Upload and save images on server' }, 'delete_image': { 'title': 'Delete image', 'description': 'Delete one image file and data' }, 'find_file': { 'title': 'Find file', 'description': 'Find and list files' }, 'upload_file': { 'title': 'Upload file', 'description': 'Upload and save files on server' }, 'delete_file': { 'title': 'Delete file', 'description': 'Delete one file file and data' }, 'find_user_image': { 'title': 'Find user images', 'description': ' ' }, 'find_all_system_images': { 'title': 'Find all system images', 'description': ' ' } }, upload: { // defaultImageStorage: null, // defaultFileStorage: null, file: {}, image: { avaibleStyles: styles, styles: { thumbnail: { width: '75', heigth: '75' }, medium: { width: '250', heigth: '250' }, large: { width: '640', heigth: '640' } } } } }) const pPoutes = { // get logged in user avatar 'get /avatar/:id([0-9]+)': { controller: 'avatar', action: 'getAvatar', permission: 'find_user' }, 'post /api/v1/user/:id([0-9]+)/avatar': { controller: 'avatar', action: 'changeAvatar', model: 'user', loadRecord: true, permission: 'update_user' }, // // -- Images routes // 'get /api/v1/image': { controller: 'image', action: 'find', model: 'image', responseType: 'json', permission: 'find_image' }, 'get /api/v1/image/:name': { controller: 'image', action: 'findOne', model: 'image', responseType: 'json', permission: 'find_image' }, // upload one image 'post /api/v1/image': { controller: 'image', action: 'create', model: 'image', responseType: 'json', permission: 'upload_image', upload: { isImage: true, // limmit settings used in multer({ limits: '' }) limits: { fieldNameSize: 150, fileSize: 10 * 1000000, // 10MB fieldSize: 20 * 1000000 // 20MB }, // file filter function used in multer({ fileFilter: fn }) fileFilter(req, file, cb) { // The function should call `cb` with a boolean // to indicate if the file should be accepted if (imageMimeTypes.indexOf(file.mimetype) < 0) { req.we.log.warn('Image:onFileUploadStart: Invalid file type for file:', file) // cancel upload on invalid type return cb(null, false) } // To accept the file pass `true`, like so: cb(null, true) }, // only accept one imagem in image param fields: [{ name: 'image', maxCount: 1 }] } }, 'post /api/v1/image/:imageId': { name: 'update_image', controller: 'image', action: 'edit', model: 'image', responseType: 'json', permission: 'updade_image' }, 'delete /api/v1/image/:name': { controller: 'image', action: 'destroy', model: 'image', responseType: 'json', permission: 'delete_image' }, // file urls: 'get /api/v1/file/:id([0-9]+)': { controller: 'file', action: 'findOne', model: 'file', responseType: 'json', permission: 'find_file' }, 'get /api/v1/file-download/:name': { controller: 'file', action: 'download', model: 'file', responseType: 'json', permission: 'find_file' }, 'post /api/v1/file': { controller: 'file', action: 'create', model: 'file', responseType: 'json', permission: 'upload_file', upload: { isLocalStorage: true, limits: { fieldNameSize: 150, files: 1, fileSize: 10 * 1000000, // 10MB fieldSize: 20 * 1000000 // 20MB }, // only accept one file in file param fields: [{ name: 'file', maxCount: 1 }] } }, 'post /api/v1/file/:fileId': { name: 'update_file', controller: 'file', action: 'edit', model: 'file', responseType: 'json', permission: 'updade_file' }, // user image routes 'get /user/:userId/image': { controller: 'image', action: 'find', model: 'image', permission: 'find_user_image', search: { currentUserIs: { parser: 'paramIs', param: 'userId', runIfNull: true, target: { type: 'field', field: 'creatorId' } } } }, 'get /api/v1/:type(file|image)/get-form-modal-content': { controller: 'file', action: 'getFormModalContent', model: 'file', responseType: 'modal', permission: true }, 'delete /api/v1/file/:name': { controller: 'file', action: 'destroy', model: 'file', responseType: 'json', permission: 'delete_file' } }; // Image style thumbnail | medium | large pPoutes['get /api/v1/image/:style('+styles.concat('original').join('|')+')/:name'] = { controller: 'image', action: 'findOne', model: 'image', responseType: 'json', permission: 'find_image' }; pPoutes['get /api/v1/image/:id([0-9]+)/data'] = { controller: 'image', action: 'findOneReturnData', model: 'image', responseType: 'json', loadRecord: true, permission: 'find_image' }; // Set plugin routes plugin.setRoutes(pPoutes); // Plugin resources: plugin.setResource({ name: 'image', namespace: '/api/v2', }); /** * Plugin fast loader for speed up we.js projeto bootstrap * * @param {Object} we * @param {Function} done callback */ plugin.fastLoader = function fastLoader(we, done) { // - Controllers: we.controllers.avatar = new we.class.Controller(require('./server/controllers/avatar.js')); we.controllers.file = new we.class.Controller(require('./server/controllers/file.js')); we.controllers.image = new we.class.Controller(require('./server/controllers/image.js')); // - Models we.db.modelsConfigs.file = require('./server/models/file.js')(we); we.db.modelsConfigs.fileassoc = require('./server/models/fileassoc.js')(we); we.db.modelsConfigs.image = require('./server/models/image.js')(we); we.db.modelsConfigs.imageassoc = require('./server/models/imageassoc.js')(we); done(); }; // // - Plugin functions // /** * Get plugin uploader middleware * * @param {Object} uploadConfigs options * @return {Function} Express middleware */ plugin.uploader = function getUploader (uploadConfigs) { return multer(uploadConfigs).fields(uploadConfigs.fields); }; /** * Build and set upload middleware * * @param {Object} data {we: app, middlewares: middlewares, config: config} */ plugin.setUploadMiddleware = function setUploadMiddleware (data) { const we = data.we; let config = data.config; let middlewares = data.middlewares; // bind upload if have upload config and after ACL check if (config.upload) { let storageName = config.upload.storageName; if (!storageName) { if (config.upload.isImage) { storageName = we.config.upload.defaultImageStorage; } else if(config.upload.isVideo) { storageName = we.config.upload.defaultVideoStorage; } else if(config.upload.isAudio) { storageName = we.config.upload.defaultAudioStorage; } else { storageName = we.config.upload.defaultFileStorage; } } if (!config.upload.storage && storageName) { let storageStrategy = we.config.upload.storages[storageName]; if ( !storageStrategy || !storageStrategy.getStorage ) { we.log.warn('we-plugin-file:storage not found in we.config.upload.storages: ' + storageName); return; } // storage.getUrlFromFile is required if (!storageStrategy.getUrlFromFile) { we.log.warn('we-plugin-file:we.config.upload.storages["' + storageName + '"].getUrlFromFile is required'); return; } config.upload.storage = storageStrategy.getStorage(we); config.storageStrategy = storageStrategy; } middlewares.push(plugin.uploader(config.upload)); } }; /** * Get sequelize file field getter function * * @param {String} fieldName * @return {Function} */ plugin.getFieldSetter = function getFieldSetter (fieldName) { return function setFiles (val) { if (plugin.we.utils._.isArray(val)) { let newVal = [] // skip flags and invalid values for (let i = 0; i < val.length; i++) { if (val[i] && val[i] !== 'null') newVal.push(val[i]) } this.setDataValue(fieldName, newVal) } else if (val && val !== 'null') { this.setDataValue(fieldName, [val]) } else { this.setDataValue(fieldName, null) } } } /** * Get sequelize file field setter function * * @param {String} fieldName * @return {Function} */ plugin.getFieldGetter = function getFieldGetter (fieldName) { return function getFiles () { // return the value or a empty array return this.getDataValue(fieldName) || [] } } /** * Set one model file or image field * * @param {Object} model we.db.models.[model] * @param {String} name field name * @param {Object} opts file field options * @param {Object} we we.js object * @param {String} ffield form field type */ plugin.setModelFileField = function setModelFileField (model, name, opts, we, ffield) { if (model.definition[name]) { we.log.verbose('Field already defined for file field:', name) return } // set field configs let cfgs = we.utils._.clone(opts) cfgs.type = we.db.Sequelize.VIRTUAL // set virtual setter cfgs.set = plugin.getFieldSetter(name, cfgs) // set virtual getter cfgs.get = plugin.getFieldGetter(name, cfgs) // set form field html cfgs.formFieldType = ffield //'file/file' // set virtual fields for term fields if not exists model.definition[name] = cfgs } plugin.setModelsFileField = function setModelsFileField (we, done) { let f, models = we.db.modelsConfigs for (var modelName in models) { if (models[modelName].options) { if (models[modelName].options.fileFields) { // file fields for (f in models[modelName].options.fileFields) { plugin.setModelFileField ( models[modelName], f, models[modelName].options.fileFields[f], we, 'file/file' ) } } if (models[modelName].options.imageFields) { for (f in models[modelName].options.imageFields) { plugin.setModelFileField ( models[modelName], f, models[modelName].options.imageFields[f], we, 'file/image' ) } } if (models[modelName].options.videoFields) { for (f in models[modelName].options.videoFields) { plugin.setModelFileField ( models[modelName], f, models[modelName].options.videoFields[f], we, 'file/video' ) } } if (models[modelName].options.audioFields) { for (f in models[modelName].options.audioFields) { plugin.setModelFileField ( models[modelName], f, models[modelName].options.audioFields[f], we, 'file/audio' ) } } } } done() } // // -- Events // plugin.events.on('we:after:load:express', function (we) { // your code here ... if (!we.config.upload.defaultImageStorage) { we.log.warn('we-plugin-file: install a file storage plugin and configure the '+ 'we.config.upload.defaultImageStorage with you storageStrategy') return; } if (!we.config.upload.defaultFileStorage) { we.log.warn('we-plugin-file: install a file storage plugin and configure the '+ 'we.config.upload.defaultFileStorage with storageStrategy') return; } }); plugin.events.on('router:before:set:controller:middleware', plugin.setUploadMiddleware) plugin.hooks.on('we:models:before:instance', plugin.setModelsFileField) // // - Plugin assets // - Only works if we-plugin-view is installed // plugin.addJs('we.component.imageSelector', { weight: 20, pluginName: 'we-plugin-file', path: 'files/public/we.components.imageSelector.js' }) plugin.addJs('we.component.fileSelector', { weight: 20, pluginName: 'we-plugin-file', path: 'files/public/we.components.fileSelector.js' }) plugin.addJs('jquery.fancybox', { weight: 15, pluginName: 'we-plugin-file', path: 'files/public/fancyBox/source/jquery.fancybox.pack.js' }) plugin.addCss('jquery.fancybox', { weight: 15, pluginName: 'we-plugin-file', path: 'files/public/fancyBox/source/jquery.fancybox.css' }) // - jquery.fileupload // plugin.addJs('jquery.iframe-transport', { weight: 7, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/js/jquery.iframe-transport.js' }) plugin.addJs('jquery.fileupload', { weight: 7, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/js/jquery.fileupload.js' }) plugin.addCss('jquery.fileupload', { weight: 7, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/css/jquery.fileupload.css' }) plugin.addJs('jquery.fileupload-process', { weight: 8, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/js/jquery.fileupload-process.js' }) plugin.addJs('jquery.fileupload-image', { weight: 8, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/js/jquery.fileupload-image.js' }) plugin.addJs('jquery.fileupload-audio', { weight: 8, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/js/jquery.fileupload-audio.js' }) plugin.addJs('jquery.fileupload-video', { weight: 8, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/js/jquery.fileupload-video.js' }) plugin.addJs('jquery.fileupload-validate', { weight: 8, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/js/jquery.fileupload-validate.js' }) plugin.addJs('jquery.fileupload-ui', { weight: 8, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/js/jquery.fileupload-ui.js' }) plugin.addJs('jquery.fileupload-jquery-ui', { weight: 8, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/js/jquery.fileupload-jquery-ui.js' }) plugin.addCss('jquery.fileupload-jquery-ui', { weight: 8, pluginName: 'we-plugin-file', path: 'files/public/jquery.fileupload/css/jquery.fileupload-ui.css' }) return plugin; }