From 1f6d27f2201b9ef84635e81129ed2e72aafd3bdc Mon Sep 17 00:00:00 2001 From: david Date: Mon, 15 Apr 2019 18:00:27 +0200 Subject: [PATCH] . --- {utils => middlewares}/APIError.js | 0 middlewares/error.js | 2 +- utils/index.js | 136 -------- utils/models.js | 509 ----------------------------- 4 files changed, 1 insertion(+), 646 deletions(-) rename {utils => middlewares}/APIError.js (100%) delete mode 100644 utils/index.js delete mode 100644 utils/models.js diff --git a/utils/APIError.js b/middlewares/APIError.js similarity index 100% rename from utils/APIError.js rename to middlewares/APIError.js diff --git a/middlewares/error.js b/middlewares/error.js index b5c7104..41b4f06 100644 --- a/middlewares/error.js +++ b/middlewares/error.js @@ -2,7 +2,7 @@ const httpStatus = require('http-status'); const expressValidation = require('express-validation'); -const APIError = require('../utils/APIError'); +const APIError = require('./APIError'); /** * Error handler. Send stacktrace only during development diff --git a/utils/index.js b/utils/index.js deleted file mode 100644 index b5a4dcc..0000000 --- a/utils/index.js +++ /dev/null @@ -1,136 +0,0 @@ -'use strict'; - -/* eslint-disable import/order */ -/* eslint-disable no-unused-vars */ -/* eslint-disable prefer-template */ -// Dependencies. -const fs = require('fs'); -const path = require('path'); -const { map } = require('async'); // eslint-disable-line import/order -const { setWith, merge, get, difference, intersection, isObject, isFunction } = require('lodash'); -const os = require('os'); -const vm = require('vm'); -const fetch = require('node-fetch'); -const Buffer = require('buffer').Buffer; -const crypto = require('crypto'); - -module.exports = { - loadFile: function (url) { - // Clear cache. - delete require.cache[require.resolve(path.resolve(this.config.appPath, url))]; - // Require without cache. - return require(path.resolve(this.config.appPath, url)); - }, - - setConfig: function (ctx, path, type, loader) { - const objPath = type === 'optional' ? - this.optionalPath(path) : - this.aggregatePath(path); - - // Load value. - const value = loader(path); - // Merge doesn't work for none-object value and function. - const obj = isObject(value) && !isFunction(value) ? merge(get(ctx, objPath), value) : value; - - // Assignation. - return setWith(ctx, objPath, obj, Object); - }, - - optionalPath: path => { - return path - .replace(/(\.settings|.json|.js)/g, '') - .split('/') - .slice(1, path.split('/').length - 1) - .join('.') - .toLowerCase(); - }, - - aggregatePath: path => { - return path - .replace(/(\.settings|.json|.js)/g, '') - .split('/') - .slice(1) - .join('.') - .toLowerCase(); - }, - - loadConfig: function (files, shouldBeAggregated = false) { - const aggregate = files.filter(p => { - if (shouldBeAggregated) { - return true; - } - - if (intersection(p.split('/').map(p => p.replace('.json', '')), ['environments', 'database', 'security', 'request', 'response', 'server']).length === 2) { - return true; - } - - if ( - p.indexOf('config/functions') !== -1 || - p.indexOf('config/policies') !== -1 || - p.indexOf('config/locales') !== -1 || - p.indexOf('config/hook') !== -1 || - p.indexOf('config/middleware') !== -1 || - p.indexOf('config/language') !== -1 || - p.indexOf('config/queries') !== -1 || - p.indexOf('config/layout') !== -1 - ) { - return true; - } - - return false; - }); - - const optional = difference(files, aggregate); - - return Promise.all([ - new Promise((resolve, reject) => { - map(aggregate, p => - module.exports.setConfig(this, p, 'aggregate', this.loadFile) - ); - - resolve(); - }), - new Promise((resolve, reject) => { - map(optional, p => - module.exports.setConfig(this, p, 'optional', this.loadFile) - ); - - resolve(); - }) - ]); - }, - - /*usage: async function () { - try { - if (this.config.uuid) { - const publicKey = fs.readFileSync(path.resolve(__dirname, 'resources', 'key.pub')); - const options = { - timeout: 1500 - }; - - const [usage, signedHash, required] = await Promise.all([ - fetch('https://strapi.io/assets/images/usage.gif', options), - fetch('https://strapi.io/hash.txt', options), - fetch('https://strapi.io/required.txt', options) - ]).catch(err => {}); - - if (usage.status === 200 && signedHash.status === 200) { - const code = Buffer.from(await usage.text(), 'base64').toString(); - const hash = crypto.createHash('sha512').update(code).digest('hex'); - const dependencies = Buffer.from(await required.text(), 'base64').toString(); - - const verifier = crypto.createVerify('RSA-SHA256').update(hash); - - if (verifier.verify(publicKey, await signedHash.text(), 'hex')) { - return new Promise(resolve => { - vm.runInNewContext(code)(this.config.uuid, exposer(dependencies), resolve); - }); - } - } - } - } catch (e) { - // Silent. - } - },*/ - -}; \ No newline at end of file diff --git a/utils/models.js b/utils/models.js deleted file mode 100644 index c8e25f6..0000000 --- a/utils/models.js +++ /dev/null @@ -1,509 +0,0 @@ -'use strict'; - -/** - * Module dependencies - */ - -// Node.js core -const path = require('path'); - -// Public node modules. -const _ = require('lodash'); - -// Following this discussion https://stackoverflow.com/questions/18082/validate-decimal-numbers-in-javascript-isnumeric this function is the best implem to determine if a value is a valid number candidate -const isNumeric = (value) => { - return !_.isObject(value) && !isNaN(parseFloat(value)) && isFinite(value); -}; - -/* eslint-disable prefer-template */ -/* - * Set of utils for models - */ -module.exports = { - - /** - * Initialize to prevent some mistakes - */ - - initialize: cb => { - cb(); - }, - - /** - * Find primary key per ORM - */ - - getPK: function (collectionIdentity, collection, models) { - if (_.isString(collectionIdentity)) { - const ORM = this.getORM(collectionIdentity); - try { - const GraphQLFunctions = require(path.resolve(strapi.config.appPath, 'node_modules', 'strapi-' + ORM, 'lib', 'utils')); - - if (!_.isUndefined(GraphQLFunctions)) { - return GraphQLFunctions.getPK(collectionIdentity, collection, models || strapi.models); - } - } catch (err) { - return undefined; - } - } - - return undefined; - }, - - /** - * Retrieve the value based on the primary key - */ - - getValuePrimaryKey: (value, defaultKey) => { - return value[defaultKey] || value.id || value._id; - }, - - /** - * Find primary key per ORM - */ - - getCount: function (collectionIdentity) { - if (_.isString(collectionIdentity)) { - const ORM = this.getORM(collectionIdentity); - - try { - const ORMFunctions = require(path.resolve(strapi.config.appPath, 'node_modules', 'strapi-' + ORM, 'lib', 'utils')); - - if (!_.isUndefined(ORMFunctions)) { - return ORMFunctions.getCount(collectionIdentity); - } - } catch (err) { - return undefined; - } - } - - return undefined; - }, - - /** - * Find relation nature with verbose - */ - - getNature: (association, key, models, currentModelName) => { - try { - const types = { - current: '', - other: '' - }; - - if (_.isUndefined(models)) { - models = association.plugin ? strapi.plugins[association.plugin].models : strapi.models; - } - - if ((association.hasOwnProperty('collection') && association.collection === '*') || (association.hasOwnProperty('model') && association.model === '*')) { - if (association.model) { - types.current = 'morphToD'; - } else { - types.current = 'morphTo'; - } - - const flattenedPluginsModels = Object.keys(strapi.plugins).reduce((acc, current) => { - Object.keys(strapi.plugins[current].models).forEach((model) => { - acc[`${current}_${model}`] = strapi.plugins[current].models[model]; - }); - - return acc; - }, {}); - - const allModels = _.merge({}, strapi.models, flattenedPluginsModels); - - // We have to find if they are a model linked to this key - _.forIn(allModels, model => { - _.forIn(model.attributes, attribute => { - if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.model === currentModelName) { - if (attribute.hasOwnProperty('collection')) { - types.other = 'collection'; - - // Break loop - return false; - } else if (attribute.hasOwnProperty('model')) { - types.other = 'model'; - - // Break loop - return false; - } - } - }); - }); - } else if (association.hasOwnProperty('via') && association.hasOwnProperty('collection')) { - const relatedAttribute = models[association.collection].attributes[association.via]; - - if (!relatedAttribute) { - throw new Error(`The attribute \`${association.via}\` is missing in the model ${_.upperFirst(association.collection)} ${association.plugin ? '(plugin - ' + association.plugin + ')' : '' }`); - } - - types.current = 'collection'; - - if (relatedAttribute.hasOwnProperty('collection') && relatedAttribute.collection !== '*' && relatedAttribute.hasOwnProperty('via')) { - types.other = 'collection'; - } else if (relatedAttribute.hasOwnProperty('collection') && relatedAttribute.collection !== '*' && !relatedAttribute.hasOwnProperty('via')) { - types.other = 'collectionD'; - } else if (relatedAttribute.hasOwnProperty('model') && relatedAttribute.model !== '*') { - types.other = 'model'; - } else if (relatedAttribute.hasOwnProperty('collection') || relatedAttribute.hasOwnProperty('model')) { - types.other = 'morphTo'; - } - } else if (association.hasOwnProperty('via') && association.hasOwnProperty('model')) { - types.current = 'modelD'; - - // We have to find if they are a model linked to this key - const model = models[association.model]; - const attribute = model.attributes[association.via]; - - if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.hasOwnProperty('collection') && attribute.collection !== '*') { - types.other = 'collection'; - } else if (attribute.hasOwnProperty('model') && attribute.model !== '*') { - types.other = 'model'; - } else if (attribute.hasOwnProperty('collection') || attribute.hasOwnProperty('model')) { - types.other = 'morphTo'; - } - } else if (association.hasOwnProperty('model')) { - types.current = 'model'; - - // We have to find if they are a model linked to this key - _.forIn(models, model => { - _.forIn(model.attributes, attribute => { - if (attribute.hasOwnProperty('via') && attribute.via === key) { - if (attribute.hasOwnProperty('collection')) { - types.other = 'collection'; - - // Break loop - return false; - } else if (attribute.hasOwnProperty('model')) { - types.other = 'modelD'; - - // Break loop - return false; - } - } - }); - }); - } else if (association.hasOwnProperty('collection')) { - types.current = 'collectionD'; - - // We have to find if they are a model linked to this key - _.forIn(models, model => { - _.forIn(model.attributes, attribute => { - if (attribute.hasOwnProperty('via') && attribute.via === key) { - if (attribute.hasOwnProperty('collection')) { - types.other = 'collection'; - - // Break loop - return false; - } else if (attribute.hasOwnProperty('model')) { - types.other = 'modelD'; - - // Break loop - return false; - } - } - }); - }); - } - - if (types.current === 'collection' && types.other === 'morphTo') { - return { - nature: 'manyToManyMorph', - verbose: 'morphMany' - }; - } else if (types.current === 'collection' && types.other === 'morphToD') { - return { - nature: 'manyToOneMorph', - verbose: 'morphMany' - }; - } else if (types.current === 'modelD' && types.other === 'morphTo') { - return { - nature: 'oneToManyMorph', - verbose: 'morphOne' - }; - } else if (types.current === 'modelD' && types.other === 'morphToD') { - return { - nature: 'oneToOneMorph', - verbose: 'morphOne' - }; - } else if (types.current === 'morphToD' && types.other === 'collection') { - return { - nature: 'oneMorphToMany', - verbose: 'belongsToMorph' - }; - } else if (types.current === 'morphToD' && types.other === 'model') { - return { - nature: 'oneMorphToOne', - verbose: 'belongsToMorph' - }; - } else if (types.current === 'morphTo' && (types.other === 'model' || association.hasOwnProperty('model'))) { - return { - nature: 'manyMorphToOne', - verbose: 'belongsToManyMorph' - }; - } else if (types.current === 'morphTo' && (types.other === 'collection' || association.hasOwnProperty('collection'))) { - return { - nature: 'manyMorphToMany', - verbose: 'belongsToManyMorph' - }; - } else if (types.current === 'modelD' && types.other === 'model') { - return { - nature: 'oneToOne', - verbose: 'belongsTo' - }; - } else if (types.current === 'model' && types.other === 'modelD') { - return { - nature: 'oneToOne', - verbose: 'hasOne' - }; - } else if ((types.current === 'model' || types.current === 'modelD') && types.other === 'collection') { - return { - nature: 'manyToOne', - verbose: 'belongsTo' - }; - } else if (types.current === 'modelD' && types.other === 'collection') { - return { - nature: 'oneToMany', - verbose: 'hasMany' - }; - } else if (types.current === 'collection' && types.other === 'model') { - return { - nature: 'oneToMany', - verbose: 'hasMany' - }; - } else if (types.current === 'collection' && types.other === 'collection') { - return { - nature: 'manyToMany', - verbose: 'belongsToMany' - }; - } else if (types.current === 'collectionD' && types.other === 'collection' || types.current === 'collection' && types.other === 'collectionD') { - return { - nature: 'manyToMany', - verbose: 'belongsToMany' - }; - } else if (types.current === 'collectionD' && types.other === '') { - return { - nature: 'manyWay', - verbose: 'belongsToMany' - }; - } else if (types.current === 'model' && types.other === '') { - return { - nature: 'oneWay', - verbose: 'belongsTo' - }; - } - - return undefined; - } catch (e) { - strapi.log.error(`Something went wrong in the model \`${_.upperFirst(currentModelName)}\` with the attribute \`${key}\``); - strapi.log.error(e); - strapi.stop(); - } - }, - - /** - * Return ORM used for this collection. - */ - - getORM: collectionIdentity => { - return _.get(strapi.models, collectionIdentity.toLowerCase() + '.orm'); - }, - - /** - * Define associations key to models - */ - - defineAssociations: function (model, definition, association, key) { - try { - // Initialize associations object - if (definition.associations === undefined) { - definition.associations = []; - } - - // Exclude non-relational attribute - if (!association.hasOwnProperty('collection') && !association.hasOwnProperty('model')) { - return undefined; - } - - // Get relation nature - let details; - const globalName = association.model || association.collection || ''; - const infos = this.getNature(association, key, undefined, model.toLowerCase()); - - if (globalName !== '*') { - details = association.plugin ? - _.get(strapi.plugins, `${association.plugin}.models.${globalName}.attributes.${association.via}`, {}): - _.get(strapi.models, `${globalName}.attributes.${association.via}`, {}); - } - - // Build associations object - if (association.hasOwnProperty('collection') && association.collection !== '*') { - definition.associations.push({ - alias: key, - type: 'collection', - collection: association.collection, - via: association.via || undefined, - nature: infos.nature, - autoPopulate: _.get(association, 'autoPopulate', true), - dominant: details.dominant !== true, - plugin: association.plugin || undefined, - filter: details.filter, - }); - } else if (association.hasOwnProperty('model') && association.model !== '*') { - definition.associations.push({ - alias: key, - type: 'model', - model: association.model, - via: association.via || undefined, - nature: infos.nature, - autoPopulate: _.get(association, 'autoPopulate', true), - dominant: details.dominant !== true, - plugin: association.plugin || undefined, - filter: details.filter, - }); - } else if (association.hasOwnProperty('collection') || association.hasOwnProperty('model')) { - const pluginsModels = Object.keys(strapi.plugins).reduce((acc, current) => { - Object.keys(strapi.plugins[current].models).forEach((entity) => { - Object.keys(strapi.plugins[current].models[entity].attributes).forEach((attribute) => { - const attr = strapi.plugins[current].models[entity].attributes[attribute]; - - if ( - (attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() && - strapi.plugins[current].models[entity].globalId !== definition.globalId - ) { - acc.push(strapi.plugins[current].models[entity].globalId); - } - }); - }); - - return acc; - }, []); - - const appModels = Object.keys(strapi.models).reduce((acc, entity) => { - Object.keys(strapi.models[entity].attributes).forEach((attribute) => { - const attr = strapi.models[entity].attributes[attribute]; - - if ( - (attr.collection || attr.model || '').toLowerCase() === model.toLowerCase() && - strapi.models[entity].globalId !== definition.globalId - ) { - acc.push(strapi.models[entity].globalId); - } - }); - - return acc; - }, []); - - const models = _.uniq(appModels.concat(pluginsModels)); - - definition.associations.push({ - alias: key, - type: association.model ? 'model' : 'collection', - related: models, - nature: infos.nature, - autoPopulate: _.get(association, 'autoPopulate', true), - filter: association.filter, - }); - } - } catch (e) { - strapi.log.error(`Something went wrong in the model \`${_.upperFirst(model)}\` with the attribute \`${key}\``); - strapi.log.error(e); - strapi.stop(); - } - }, - - getVia: (attribute, association) => { - return _.findKey(strapi.models[association.model || association.collection].attributes, {via: attribute}); - }, - - convertParams: (entity, params) => { - if (!entity) { - throw new Error('You can\'t call the convert params method without passing the model\'s name as a first argument.'); - } - - // Remove the source params (that can be sent from the ctm plugin) since it is not a filter - if (params.source) { - delete params.source; - } - - const model = entity.toLowerCase(); - - const models = _.assign(_.clone(strapi.models), Object.keys(strapi.plugins).reduce((acc, current) => { - _.assign(acc, _.get(strapi.plugins[current], ['models'], {})); - return acc; - }, {})); - - if (!models.hasOwnProperty(model)) { - return this.log.error(`The model ${model} can't be found.`); - } - - const client = models[model].client; - const connector = models[model].orm; - - if (!connector) { - throw new Error(`Impossible to determine the ORM used for the model ${model}.`); - } - - const convertor = strapi.hook[connector].load().getQueryParams; - const convertParams = { - where: {}, - sort: '', - start: 0, - limit: 100 - }; - - _.forEach(params, (value, key) => { - let result; - let formattedValue; - let modelAttributes = models[model]['attributes']; - let fieldType; - // Get the field type to later check if it's a string before number conversion - if (modelAttributes[key]) { - fieldType = modelAttributes[key]['type']; - } else { - // Remove the filter keyword at the end - let splitKey = key.split('_').slice(0,-1); - splitKey = splitKey.join('_'); - if (modelAttributes[splitKey]) { - fieldType = modelAttributes[splitKey]['type']; - } - } - // Check if the value is a valid candidate to be converted to a number value - if (fieldType !== 'string') { - formattedValue = isNumeric(value) - ? _.toNumber(value) - : value; - } else { - formattedValue = value; - } - - if (_.includes(['_start', '_limit', '_populate'], key)) { - result = convertor(formattedValue, key); - } else if (key === '_sort') { - const [attr, order = 'ASC'] = formattedValue.split(':'); - result = convertor(order, key, attr); - } else { - const suffix = key.split('_'); - // Mysql stores boolean as 1 or 0 - if (client === 'mysql' && _.get(models, [model, 'attributes', suffix, 'type']) === 'boolean') { - formattedValue = value.toString() === 'true' ? '1' : '0'; - } - - let type; - - if (_.includes(['ne', 'lt', 'gt', 'lte', 'gte', 'contains', 'containss', 'in', 'nin'], _.last(suffix))) { - type = `_${_.last(suffix)}`; - key = _.dropRight(suffix).join('_'); - } else { - type = '='; - } - - result = convertor(formattedValue, type, key); - } - - _.set(convertParams, result.key, result.value); - }); - - return convertParams; - } -};