Merge branch 'master' into move-frontend-to-src
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
const validate = require('express-validation');
|
||||
const Joi = require('joi');
|
||||
import validate from 'express-validation';
|
||||
import Joi from 'joi';
|
||||
|
||||
module.exports = {
|
||||
login: validate({
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
const httpStatus = require('http-status');
|
||||
|
||||
/**
|
||||
* @extends Error
|
||||
*/
|
||||
class ExtendableError extends Error {
|
||||
constructor(message, status, isPublic) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.isPublic = isPublic;
|
||||
this.isOperational = true; // This is required since bluebird 4 doesn't append it anymore.
|
||||
Error.captureStackTrace(this, this.constructor.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing an API error.
|
||||
* @extends ExtendableError
|
||||
*/
|
||||
class APIError extends ExtendableError {
|
||||
/**
|
||||
* Creates an API error.
|
||||
* @param {string} message - Error message.
|
||||
* @param {number} status - HTTP status code of error.
|
||||
* @param {boolean} isPublic - Whether the message should be visible to user or not.
|
||||
*/
|
||||
constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, isPublic = false) {
|
||||
super(message, status, isPublic);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = APIError;
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
/**
|
||||
* @extends Error
|
||||
*/
|
||||
class ExtendableError extends Error {
|
||||
constructor(message, status, isPublic) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.isPublic = isPublic;
|
||||
this.isOperational = true; // This is required since bluebird 4 doesn't append it anymore.
|
||||
Error.captureStackTrace(this, this.constructor.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing an API error.
|
||||
* @extends ExtendableError
|
||||
*/
|
||||
class APIError extends ExtendableError {
|
||||
/**
|
||||
* Creates an API error.
|
||||
* @param {string} message - Error message.
|
||||
* @param {number} status - HTTP status code of error.
|
||||
* @param {boolean} isPublic - Whether the message should be visible to user or not.
|
||||
*/
|
||||
constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, isPublic = false) {
|
||||
super(message, status, isPublic);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = APIError;
|
||||
|
||||
13
src/index.js
13
src/index.js
@@ -21,13 +21,17 @@ import authValidate from './auth/validate';
|
||||
import authRequestHandlers from './auth/requestHandlers';
|
||||
import passwordResetConfig from './auth/passwordResets/passwordReset.config';
|
||||
import validateConfig from './utilities/validateConfig';
|
||||
import setModelLocaleMiddleware from './mongoose/setModelLocale.middleware';
|
||||
|
||||
class Payload {
|
||||
|
||||
models = {};
|
||||
|
||||
constructor(options) {
|
||||
mongoose.connect(options.config.mongoURL, { useNewUrlParser: true }, (err) => {
|
||||
mongoose.connect(options.config.mongoURL, {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
}, (err) => {
|
||||
if (err) {
|
||||
console.log('Unable to connect to the Mongo server. Please start the server. Error:', err);
|
||||
} else {
|
||||
@@ -130,13 +134,16 @@ class Payload {
|
||||
|
||||
if (config.auth.registration) {
|
||||
options.router
|
||||
.route('/' + config.slug + '/register') // TODO: not sure how to incorporate url params like `:pageId`
|
||||
.route(`${config.slug}/register`) // TODO: not sure how to incorporate url params like `:pageId`
|
||||
.post(config.auth.registrationValidation, auth.register);
|
||||
}
|
||||
}
|
||||
|
||||
this.models[config.labels.singular] = model;
|
||||
options.router.all(`/${config.slug}*`, bindModelMiddleware(model));
|
||||
options.router.all(`/${config.slug}*`,
|
||||
bindModelMiddleware(model),
|
||||
setModelLocaleMiddleware()
|
||||
);
|
||||
|
||||
options.router.route(`/${config.slug}`)
|
||||
.get(config.policies.read, query)
|
||||
|
||||
@@ -6,8 +6,8 @@ export default function localizationPlugin(schema, options) {
|
||||
}
|
||||
|
||||
// plugin options to be set under schema options
|
||||
schema.options.mongooseIntl = {};
|
||||
const pluginOptions = schema.options.mongooseIntl;
|
||||
schema.options.localization = {};
|
||||
const pluginOptions = schema.options.localization;
|
||||
|
||||
pluginOptions.locales = options.locales.slice(0);
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function localizationPlugin(schema, options) {
|
||||
.set(function (value) {
|
||||
// multiple locales are set as an object
|
||||
if (typeof value === 'object') {
|
||||
let locales = this.schema.options.mongooseIntl.locales;
|
||||
let locales = this.schema.options.localization.locales;
|
||||
locales.forEach(locale => {
|
||||
if (!value[locale]) {
|
||||
return;
|
||||
@@ -137,10 +137,10 @@ export default function localizationPlugin(schema, options) {
|
||||
// document methods to set the locale for each model instance (document)
|
||||
schema.method({
|
||||
getLocales: function () {
|
||||
return this.schema.options.mongooseIntl.locales;
|
||||
return this.schema.options.localization.locales;
|
||||
},
|
||||
getLocale: function () {
|
||||
return this.docLocale || this.schema.options.mongooseIntl.defaultLocale;
|
||||
return this.docLocale || this.schema.options.localization.defaultLocale;
|
||||
},
|
||||
setLocale: function (locale, fallbackLocale) {
|
||||
const locales = this.getLocales();
|
||||
@@ -171,15 +171,15 @@ export default function localizationPlugin(schema, options) {
|
||||
// model methods to set the locale for the current schema
|
||||
schema.static({
|
||||
getLocales: function () {
|
||||
return this.schema.options.mongooseIntl.locales;
|
||||
return this.schema.options.localization.locales;
|
||||
},
|
||||
getDefaultLocale: function () {
|
||||
return this.schema.options.mongooseIntl.defaultLocale;
|
||||
return this.schema.options.localization.defaultLocale;
|
||||
},
|
||||
setDefaultLocale: function (locale) {
|
||||
|
||||
let updateLocale = function (schema, locale) {
|
||||
schema.options.mongooseIntl.defaultLocale = locale.slice(0);
|
||||
schema.options.localization.defaultLocale = locale.slice(0);
|
||||
|
||||
// default locale change for sub-documents schemas
|
||||
schema.eachPath((path, schemaType) => {
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
import sharp from 'sharp';
|
||||
const { promisify } = require('util');
|
||||
const sizeOf = promisify(require('image-size'));
|
||||
|
||||
function getOutputImageName(sourceImage, size) {
|
||||
let extension = sourceImage.split('.').pop();
|
||||
let filenameWithoutExtension = sourceImage.substr(0, sourceImage.lastIndexOf('.')) || sourceImage;
|
||||
return `${filenameWithoutExtension}-${size.width}x${size.height}.${extension}`;
|
||||
}
|
||||
|
||||
export async function resizeAndSave(config, file) {
|
||||
let sourceImage = `${config.staticDir}/${file.name}`;
|
||||
|
||||
let outputSizes = [];
|
||||
try {
|
||||
const dimensions = await sizeOf(sourceImage);
|
||||
for (let desiredSize of config.imageSizes) {
|
||||
if (desiredSize.width > dimensions.width) {
|
||||
continue;
|
||||
}
|
||||
let outputImageName = getOutputImageName(sourceImage, desiredSize);
|
||||
await sharp(sourceImage)
|
||||
.resize(desiredSize.width, desiredSize.height, {
|
||||
position: desiredSize.crop || 'centre'
|
||||
})
|
||||
.toFile(outputImageName);
|
||||
outputSizes.push({ height: desiredSize.height, width: desiredSize.width });
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error in resize and save', e.message);
|
||||
}
|
||||
|
||||
return outputSizes;
|
||||
}
|
||||
import sharp from 'sharp';
|
||||
import { promisify } from 'util';
|
||||
import imageSize from 'image-size';
|
||||
const sizeOf = promisify(imageSize);
|
||||
|
||||
function getOutputImageName(sourceImage, size) {
|
||||
let extension = sourceImage.split('.').pop();
|
||||
let filenameWithoutExtension = sourceImage.substr(0, sourceImage.lastIndexOf('.')) || sourceImage;
|
||||
return `${filenameWithoutExtension}-${size.width}x${size.height}.${extension}`;
|
||||
}
|
||||
|
||||
export async function resizeAndSave(config, file) {
|
||||
let sourceImage = `${config.staticDir}/${file.name}`;
|
||||
|
||||
let outputSizes = [];
|
||||
try {
|
||||
const dimensions = await sizeOf(sourceImage);
|
||||
for (let desiredSize of config.imageSizes) {
|
||||
if (desiredSize.width > dimensions.width) {
|
||||
continue;
|
||||
}
|
||||
let outputImageName = getOutputImageName(sourceImage, desiredSize);
|
||||
await sharp(sourceImage)
|
||||
.resize(desiredSize.width, desiredSize.height, {
|
||||
position: desiredSize.crop || 'centre'
|
||||
})
|
||||
.toFile(outputImageName);
|
||||
outputSizes.push({ height: desiredSize.height, width: desiredSize.width });
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error in resize and save', e.message);
|
||||
}
|
||||
|
||||
return outputSizes;
|
||||
}
|
||||
|
||||
@@ -1,153 +1,149 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
module.exports = function (schema) {
|
||||
const pathsToPopulate = [];
|
||||
|
||||
eachPathRecursive(schema, (pathname, schemaType) => {
|
||||
let option;
|
||||
if (schemaType.options && schemaType.options.autopopulate) {
|
||||
option = schemaType.options.autopopulate;
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schemaType.options),
|
||||
autopopulate: option
|
||||
});
|
||||
} else if (schemaType.options &&
|
||||
schemaType.options.type &&
|
||||
schemaType.options.type[0] &&
|
||||
schemaType.options.type[0].autopopulate) {
|
||||
option = schemaType.options.type[0].autopopulate;
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schemaType.options.type[0]),
|
||||
autopopulate: option
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (schema.virtuals) {
|
||||
Object.keys(schema.virtuals).forEach((pathname) => {
|
||||
if (schema.virtuals[pathname].options.autopopulate) {
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schema.virtuals[pathname].options),
|
||||
autopopulate: schema.virtuals[pathname].options.autopopulate,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var autopopulateHandler = function () {
|
||||
if (this._mongooseOptions &&
|
||||
this._mongooseOptions.lean &&
|
||||
// If lean and user didn't explicitly do `lean({ autopulate: true })`,
|
||||
// skip it. See gh-27, gh-14, gh-48
|
||||
!this._mongooseOptions.lean.autopopulate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = this.options || {};
|
||||
if (options.autopopulate === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
let maxDepth = options.maxDepth;
|
||||
|
||||
if (options.autopopulate && options.autopopulate.maxDepth) {
|
||||
maxDepth = options.autopopulate.maxDepth;
|
||||
}
|
||||
|
||||
const depth = options._depth != null ? options._depth : 0;
|
||||
|
||||
if (options.maxDepth > 0 && depth >= maxDepth) {
|
||||
return;
|
||||
}
|
||||
|
||||
const numPaths = pathsToPopulate.length;
|
||||
for (let i = 0; i < numPaths; ++i) {
|
||||
pathsToPopulate[i].options = pathsToPopulate[i].options || {};
|
||||
pathsToPopulate[i].options.options = pathsToPopulate[i].options.options || {};
|
||||
Object.assign(pathsToPopulate[i].options.options, {
|
||||
...options,
|
||||
_depth: depth + 1
|
||||
});
|
||||
processOption.call(this,
|
||||
pathsToPopulate[i].autopopulate, pathsToPopulate[i].options);
|
||||
}
|
||||
};
|
||||
|
||||
schema.
|
||||
pre('find', autopopulateHandler).
|
||||
pre('findOne', autopopulateHandler).
|
||||
pre('findOneAndUpdate', autopopulateHandler);
|
||||
};
|
||||
|
||||
function defaultOptions(pathname, v) {
|
||||
const ret = { path: pathname, options: { maxDepth: 10 } };
|
||||
if (v.ref != null) {
|
||||
ret.model = v.ref;
|
||||
ret.ref = v.ref;
|
||||
}
|
||||
if (v.refPath != null) {
|
||||
ret.refPath = v.refPath;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function processOption(value, options) {
|
||||
switch (typeof value) {
|
||||
case 'function':
|
||||
handleFunction.call(this, value, options);
|
||||
break;
|
||||
case 'object':
|
||||
handleObject.call(this, value, options);
|
||||
break;
|
||||
default:
|
||||
handlePrimitive.call(this, value, options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handlePrimitive(value, options) {
|
||||
if (value) {
|
||||
this.populate(options);
|
||||
}
|
||||
}
|
||||
|
||||
function handleObject(value, optionsToUse) {
|
||||
// Special case: support top-level `maxDepth`
|
||||
if (value.maxDepth != null) {
|
||||
optionsToUse.options = optionsToUse.options || {};
|
||||
optionsToUse.options.maxDepth = value.maxDepth;
|
||||
delete value.maxDepth;
|
||||
}
|
||||
optionsToUse = Object.assign({}, optionsToUse, value);
|
||||
this.populate(optionsToUse);
|
||||
}
|
||||
|
||||
function handleFunction(fn, options) {
|
||||
const val = fn.call(this, options);
|
||||
processOption.call(this, val, options);
|
||||
}
|
||||
|
||||
function mergeOptions(destination, source) {
|
||||
const keys = Object.keys(source);
|
||||
const numKeys = keys.length;
|
||||
for (let i = 0; i < numKeys; ++i) {
|
||||
destination[keys[i]] = source[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
function eachPathRecursive(schema, handler, path) {
|
||||
if (!path) {
|
||||
path = [];
|
||||
}
|
||||
schema.eachPath((pathname, schemaType) => {
|
||||
path.push(pathname);
|
||||
if (schemaType.schema) {
|
||||
eachPathRecursive(schemaType.schema, handler, path);
|
||||
} else {
|
||||
handler(path.join('.'), schemaType);
|
||||
}
|
||||
path.pop();
|
||||
});
|
||||
}
|
||||
module.exports = function (schema) {
|
||||
const pathsToPopulate = [];
|
||||
|
||||
eachPathRecursive(schema, (pathname, schemaType) => {
|
||||
let option;
|
||||
if (schemaType.options && schemaType.options.autopopulate) {
|
||||
option = schemaType.options.autopopulate;
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schemaType.options),
|
||||
autopopulate: option
|
||||
});
|
||||
} else if (schemaType.options &&
|
||||
schemaType.options.type &&
|
||||
schemaType.options.type[0] &&
|
||||
schemaType.options.type[0].autopopulate) {
|
||||
option = schemaType.options.type[0].autopopulate;
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schemaType.options.type[0]),
|
||||
autopopulate: option
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (schema.virtuals) {
|
||||
Object.keys(schema.virtuals).forEach((pathname) => {
|
||||
if (schema.virtuals[pathname].options.autopopulate) {
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schema.virtuals[pathname].options),
|
||||
autopopulate: schema.virtuals[pathname].options.autopopulate,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var autopopulateHandler = function () {
|
||||
if (this._mongooseOptions &&
|
||||
this._mongooseOptions.lean &&
|
||||
// If lean and user didn't explicitly do `lean({ autopulate: true })`,
|
||||
// skip it. See gh-27, gh-14, gh-48
|
||||
!this._mongooseOptions.lean.autopopulate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = this.options || {};
|
||||
if (options.autopopulate === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
let maxDepth = options.maxDepth;
|
||||
|
||||
if (options.autopopulate && options.autopopulate.maxDepth) {
|
||||
maxDepth = options.autopopulate.maxDepth;
|
||||
}
|
||||
|
||||
const depth = options._depth != null ? options._depth : 0;
|
||||
|
||||
if (options.maxDepth > 0 && depth >= maxDepth) {
|
||||
return;
|
||||
}
|
||||
|
||||
const numPaths = pathsToPopulate.length;
|
||||
for (let i = 0; i < numPaths; ++i) {
|
||||
pathsToPopulate[i].options = pathsToPopulate[i].options || {};
|
||||
pathsToPopulate[i].options.options = pathsToPopulate[i].options.options || {};
|
||||
Object.assign(pathsToPopulate[i].options.options, {
|
||||
...options,
|
||||
_depth: depth + 1
|
||||
});
|
||||
processOption.call(this,
|
||||
pathsToPopulate[i].autopopulate, pathsToPopulate[i].options);
|
||||
}
|
||||
};
|
||||
|
||||
schema.
|
||||
pre('find', autopopulateHandler).
|
||||
pre('findOne', autopopulateHandler).
|
||||
pre('findOneAndUpdate', autopopulateHandler);
|
||||
};
|
||||
|
||||
function defaultOptions(pathname, v) {
|
||||
const ret = { path: pathname, options: { maxDepth: 10 } };
|
||||
if (v.ref != null) {
|
||||
ret.model = v.ref;
|
||||
ret.ref = v.ref;
|
||||
}
|
||||
if (v.refPath != null) {
|
||||
ret.refPath = v.refPath;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function processOption(value, options) {
|
||||
switch (typeof value) {
|
||||
case 'function':
|
||||
handleFunction.call(this, value, options);
|
||||
break;
|
||||
case 'object':
|
||||
handleObject.call(this, value, options);
|
||||
break;
|
||||
default:
|
||||
handlePrimitive.call(this, value, options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handlePrimitive(value, options) {
|
||||
if (value) {
|
||||
this.populate(options);
|
||||
}
|
||||
}
|
||||
|
||||
function handleObject(value, optionsToUse) {
|
||||
// Special case: support top-level `maxDepth`
|
||||
if (value.maxDepth != null) {
|
||||
optionsToUse.options = optionsToUse.options || {};
|
||||
optionsToUse.options.maxDepth = value.maxDepth;
|
||||
delete value.maxDepth;
|
||||
}
|
||||
optionsToUse = Object.assign({}, optionsToUse, value);
|
||||
this.populate(optionsToUse);
|
||||
}
|
||||
|
||||
function handleFunction(fn, options) {
|
||||
const val = fn.call(this, options);
|
||||
processOption.call(this, val, options);
|
||||
}
|
||||
|
||||
function mergeOptions(destination, source) {
|
||||
const keys = Object.keys(source);
|
||||
const numKeys = keys.length;
|
||||
for (let i = 0; i < numKeys; ++i) {
|
||||
destination[keys[i]] = source[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
function eachPathRecursive(schema, handler, path) {
|
||||
if (!path) {
|
||||
path = [];
|
||||
}
|
||||
schema.eachPath((pathname, schemaType) => {
|
||||
path.push(pathname);
|
||||
if (schemaType.schema) {
|
||||
eachPathRecursive(schemaType.schema, handler, path);
|
||||
} else {
|
||||
handler(path.join('.'), schemaType);
|
||||
}
|
||||
path.pop();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
const create = (req, res) => {
|
||||
req.model.setDefaultLocale(req.locale); // TODO - move to middleware
|
||||
req.model.create(req.body, (err, result) => {
|
||||
if (err)
|
||||
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err });
|
||||
|
||||
return res.status(httpStatus.CREATED)
|
||||
.json({
|
||||
message: 'success',
|
||||
result: result.toJSON({ virtuals: true })
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default create;
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
const create = (req, res) => {
|
||||
req.model.create(req.body, (err, result) => {
|
||||
if (err)
|
||||
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err });
|
||||
|
||||
return res.status(httpStatus.CREATED)
|
||||
.json({
|
||||
message: 'success',
|
||||
result: result.toJSON({ virtuals: true })
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default create;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
const update = (req, res) => {
|
||||
req.model.setDefaultLocale(req.locale); // TODO - move to middleware
|
||||
req.model.findOne({ _id: req.params.id }, '', {}, (err, doc) => {
|
||||
if (!doc)
|
||||
return res.status(httpStatus.NOT_FOUND).send('Not Found');
|
||||
|
||||
9
src/mongoose/setModelLocale.middleware.js
Normal file
9
src/mongoose/setModelLocale.middleware.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const setModelLocaleMiddleware = () => {
|
||||
return (req, res, next) => {
|
||||
if (req.locale && req.model.setDefaultLocale)
|
||||
req.model.setDefaultLocale(req.locale);
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
export default setModelLocaleMiddleware;
|
||||
@@ -1,4 +1,4 @@
|
||||
const { defaults } = require('./jest.config');
|
||||
import defaults from './jest.config';
|
||||
|
||||
module.exports = {
|
||||
...defaults,
|
||||
|
||||
Reference in New Issue
Block a user