UNPKG

we-plugin-file

Version:

We.js file plugin with suport to storages

403 lines (349 loc) 12 kB
/** * Image model * * @module :: Model * */ const sh = require('../../lib/sequelize-hooks'); module.exports = function ImageModel (we) { const _ = we.utils._, async = we.utils.async; // set sequelize model define and options const model = { definition: { // - user given data text label: { type: we.db.Sequelize.STRING }, description: { type: we.db.Sequelize.TEXT }, // - data get from file name: { type: we.db.Sequelize.STRING, allowNull: false, unique: true }, size: { type: we.db.Sequelize.INTEGER }, encoding: { type: we.db.Sequelize.STRING }, active: { type: we.db.Sequelize.BOOLEAN, defaultValue: true }, originalname: { type: we.db.Sequelize.STRING }, mime: { type: we.db.Sequelize.STRING }, extension: { type: we.db.Sequelize.STRING }, storageName: { type: we.db.Sequelize.STRING }, isLocalStorage: { type: we.db.Sequelize.BOOLEAN, defaultValue: true }, urls: { type: we.db.Sequelize.BLOB, allowNull: false, skipSanitizer: true, get() { var v = this.getDataValue('urls') if (!v) return {} if (v instanceof Buffer) { try { return JSON.parse(v.toString('utf8')) } catch (e) { we.log.error('error on parse file urls from db', e) return {} } } else if (typeof v == 'string') { return JSON.parse(v) } else { return v } }, set(v) { if (!v) v = {} if (typeof v != 'object') throw new Error('file:urls:need_be_object') this.setDataValue('urls', JSON.stringify(v)) } }, extraData: { type: we.db.Sequelize.BLOB, skipSanitizer: true, get() { var v = this.getDataValue('extraData') if (!v) return {} if (v instanceof Buffer) { try { return JSON.parse(v.toString('utf8')) } catch (e) { we.log.error('error on parse file extraData from db', e) return {} } } else if (typeof v == 'string') { return JSON.parse(v) } else { return v } }, set(v) { if (!v) v = {} if (typeof v != 'object') throw new Error('file:extraData:need_be_object') this.setDataValue('extraData', JSON.stringify(v)) } } }, associations: { creator: { type: 'belongsTo', model: 'user' } } } // after define all models, add image field hooks in models how have images we.hooks.on('we:models:set:joins', sh.addAllImageHooks); we.events.on('we:after:load:plugins', function (we) { if (!we.file) we.file = {} if (!we.file.image) we.file.image = {} const db = we.db we.file.image.getModelImageFields = function getModelImageFields(Model) { if (!Model || !Model.options || !Model.options.imageFields) return null; return Model.options.imageFields } we.file.image.afterFind = function afterFind (r, opts) { return new Promise( (resolve, reject)=> { const Model = this; // skip if is raw query that dont preload need model attrs and methods if (opts.raw) return resolve(); if (_.isArray(r)) { async.each(r, (r1, next)=> { // we.db.models.imageassoc we.file.image.afterFindRecord.bind(Model)(r1, opts, next); }, (err)=> { if (err) return reject(err); resolve(); }); } else { we.file.image.afterFindRecord.bind(Model)(r, opts, (err)=> { if (err) return reject(err); resolve(); }); } }); } we.file.image.afterFindRecord = function afterFindRecord (r, opts, done) { const functions = []; const Model = this; // found 0 results if (!r) return done(); // skip if is raw query that dont preload need model attrs and methods if (opts.raw || !r.setDataValue) return done(); const fields = we.file.image.getModelImageFields(this); if (!fields) return done(); if (!r._salvedImages) r._salvedImages = {}; if (!r._salvedImageAssocs) r._salvedImageAssocs = {}; const fieldNames = Object.keys(fields); // for each field fieldNames.forEach( (fieldName)=> { functions.push( (next)=> { return db.models.imageassoc .findAll({ where: { modelName: Model.name, modelId: r.id, field: fieldName }, include: [{ all: true }] }) .then( (imgAssocs)=> { if (_.isEmpty(imgAssocs)) { next(); return null; } r._salvedImages = imgAssocs.map( (imgAssoc)=> { return imgAssoc.image.toJSON(); }); r.setDataValue(fieldName, r._salvedImages); // salved terms cache r._salvedImageAssocs[fieldName] = imgAssocs; next(); return null; }) .catch(next); }) }) async.parallel(functions, done); } // after create one record with image fields we.file.image.afterCreatedRecord = function afterCreatedRecord (r, opts) { return new Promise( (resolve, reject)=> { const functions = []; const Model = this; // skip if is raw query that dont preload need model attrs and methods if (opts.raw || !r.setDataValue) return resolve(); const fields = we.file.image.getModelImageFields(this); if (!fields) return resolve(); const imageFields = Object.keys(fields); if (!r._salvedImages) r._salvedImages = {}; if (!r._salvedImageAssocs) r._salvedImageAssocs = {}; imageFields.forEach( (fieldName)=> { let values = r.get(fieldName); if (_.isEmpty(values)) return; const imagesToSave = []; const newImageAssocs = []; functions.push( (nextField)=> { async.each(values, (value, next)=> { if (!value || (value === 'null')) return next(); // check if the image exists db.models.image .findOne({ where: { id: value.id || value } }) .then( (i)=> { if (!i) { next(); return null; } return db.models.imageassoc .create({ modelName: Model.name, modelId: r.id, field: fieldName, imageId: value.id || value }) .then( (r)=> { we.log.verbose('Image assoc created:', r.id); imagesToSave.push(i); newImageAssocs.push(r); next(); return null; }); }) .catch(next); }, (err)=> { if (err) return nextField(err); r._salvedImageAssocs[fieldName] = newImageAssocs; r._salvedImages[fieldName] = imagesToSave; r.setDataValue(fieldName, imagesToSave.map( (im)=> { return im.toJSON(); })); nextField(); }); }); }); async.series(functions, (err)=> { if (err) return reject(err); resolve(); }); }); } // after update one record with image fields we.file.image.afterUpdatedRecord = function afterUpdatedRecord (r, opts) { return new Promise( (resolve, reject)=> { const Model = this; // skip if is raw query that dont preload need model attrs and methods if (opts.raw || !r.setDataValue) return resolve(); const fields = we.file.image.getModelImageFields(this); if (!fields) { return resolve(); } const fieldNames = Object.keys(fields); async.eachSeries(fieldNames, (fieldName, nextField)=> { // check if user whant update this field if (opts.fields.indexOf(fieldName) === -1) return nextField(); let imagesToSave = _.clone(r.get(fieldName)); let newImageAssocs = []; let newImageAssocsIds = []; async.series([ function findOrCreateAllAssocs (done) { let preloadedImagesAssocsToSave = []; async.each(imagesToSave, (its, next)=> { if (_.isEmpty(its) || its === 'null') return next(); let values = { modelName: Model.name, modelId: r.id, field: fieldName, imageId: its.id || its }; // check if this image exits db.models.image.findOne({ where: { id: its.id || its } }) .then( (i)=> { if (!i) { done(); return null; } // find of create the assoc return db.models.imageassoc .findOrCreate({ where: values, defaults: values }) .then( (r)=> { r[0].image = i; preloadedImagesAssocsToSave.push(r[0]); next(); return null; }); }) .catch(done); }, (err)=> { if (err) return done(err) imagesToSave = preloadedImagesAssocsToSave.map( (r)=> { newImageAssocsIds.push(r.id); return r.image }); newImageAssocs = preloadedImagesAssocsToSave; done(); }) }, // delete removed image assocs function deleteAssocs (done) { let query = { where: { modelName: Model.name, modelId: r.id, field: fieldName } }; if (!_.isEmpty(newImageAssocsIds)) { query.where.id = { [we.Op.notIn]: newImageAssocsIds }; } db.models.imageassoc .destroy(query) .then( (result)=> { we.log.verbose('Result from deleted image assocs: ', result, fieldName, Model.name); done(); return null; }) .catch(done); }, function setRecorValues (done) { r._salvedImages[fieldName] = imagesToSave; r._salvedImageAssocs[fieldName] = newImageAssocs; r.setDataValue(fieldName, imagesToSave.map( (im)=> { return im.toJSON(); })); done(); } ], nextField); }, (err)=> { if (err) return reject(err); resolve(); }); }); } // delete the image associations after delete related model we.file.image.afterDeleteRecord = function afterDeleteRecord (r) { return new Promise( (resolve, reject)=> { const Model = this; db.models.imageassoc .destroy({ where: { modelName: Model.name, modelId: r.id } }) .then( (result)=> { we.log.debug('Deleted ' + result + ' image assocs from record with id: ' + r.id); resolve(); return null; }) .catch(reject); }); } }); return model; }