.
This commit is contained in:
parent
a513dcb32e
commit
bbc792ed33
@ -15,5 +15,10 @@ module.exports = {
|
||||
server: {
|
||||
hostname: process.env.HOSTNAME || '127.0.0.1',
|
||||
port: process.env.PORT || 1337
|
||||
},
|
||||
|
||||
paginate: {
|
||||
limit: 10,
|
||||
maxLimit: 50
|
||||
}
|
||||
}
|
||||
|
||||
118
core/controllers/find.js
Normal file
118
core/controllers/find.js
Normal file
@ -0,0 +1,118 @@
|
||||
function extractFindOptions(req, res, targetAttributes) {
|
||||
const result = {};
|
||||
|
||||
result.params = (req && req.params) ? req.params : null;
|
||||
|
||||
if (res && res.locals) {
|
||||
Object.keys(res.locals).forEach(key => {
|
||||
result[key] = res.locals[key]
|
||||
})
|
||||
}
|
||||
|
||||
console.log(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseFindOptions(findOptions, targetAttributes) {
|
||||
const result = {};
|
||||
|
||||
// Where
|
||||
if (findOptions.params) {
|
||||
result.where = findOptions.params
|
||||
}
|
||||
|
||||
// Paginate
|
||||
if (findOptions.paginate) {
|
||||
result.page = findOptions.paginate.page;
|
||||
result.limit = findOptions.paginate.limit;
|
||||
}
|
||||
|
||||
// Order
|
||||
if (findOptions.sort) {
|
||||
result.order = [];
|
||||
Object.keys(findOptions.sort).forEach(key => {
|
||||
let dir = findOptions.sort[key] ? 'ASC' : 'DESC';
|
||||
result.order.push([key, dir])
|
||||
});
|
||||
}
|
||||
|
||||
// Attributes
|
||||
if (findOptions.fields) {
|
||||
if (findOptions.fields.validFields) {
|
||||
result.attributes = findOptions.fields.validFields
|
||||
}
|
||||
if (findOptions.fields.invalidFields && Array.isArray(findOptions.fields.invalidFields)) {
|
||||
result.attributes = targetAttributes.filter(attr => !findOptions.fields.invalidFields.includes(attr));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function checkIsModel(target) {
|
||||
return !!target.getTableName;
|
||||
}
|
||||
|
||||
function checkIsAssociation(target) {
|
||||
return !!target.associationType;
|
||||
}
|
||||
|
||||
function resultIsAList(params) {
|
||||
const _params = Object.keys(params);
|
||||
if (_params && Array.isArray(_params) && model.primaryKeyAttributes && Array.isArray(model.primaryKeyAttributes)) {
|
||||
return !model.primaryKeyAttributes.every(field => _params.includes(field));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function _find(modelMaybeThunk, options = {}) {
|
||||
/*if (typeof modelMaybeThunk != 'function' && !checkIsModel(modelMaybeThunk) && !checkIsAssociation(modelMaybeThunk)) {
|
||||
throw new Error('resolverFactory should be called with a model, an association or a function (which resolves to a model or an association)');
|
||||
}*/
|
||||
|
||||
if (options.before === undefined) options.before = (options) => options;
|
||||
if (options.after === undefined) options.after = (result) => result;
|
||||
|
||||
return async function (req, res, next) {
|
||||
let target = typeof modelMaybeThunk === 'function' && !checkIsModel(modelMaybeThunk) ?
|
||||
await Promise.resolve(modelMaybeThunk(req, res, next)) : modelMaybeThunk;
|
||||
|
||||
let isModel = checkIsModel(target),
|
||||
isAssociation = checkIsAssociation(target),
|
||||
association = isAssociation && target,
|
||||
isList = options.list || resultIsAList(req.params),
|
||||
model = isAssociation && target.target || isModel && target;
|
||||
|
||||
let findOptions = extractFindOptions(req, res);
|
||||
let targetAttributes = Object.keys(model.rawAttributes);
|
||||
|
||||
findOptions = parseFindOptions(findOptions, targetAttributes);
|
||||
|
||||
console.log(findOptions);
|
||||
|
||||
return Promise.resolve(options.before(findOptions, req, res))
|
||||
.then(findOptions => {
|
||||
return model[isList ? 'fetchAll' : 'fetch'](findOptions);
|
||||
})
|
||||
.then(result => {
|
||||
return options.after(result, req, res);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const defaultOptions = {
|
||||
//before: optimizeAttributes,
|
||||
}
|
||||
|
||||
|
||||
const find = (model, options = defaultOptions) => {
|
||||
return async function (req, res, next) {
|
||||
return _find(model, options)(req, res, next)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = find;
|
||||
91
core/controllers/index.js
Normal file
91
core/controllers/index.js
Normal file
@ -0,0 +1,91 @@
|
||||
const _find = require('./find');
|
||||
const httpStatus = require('http-status');
|
||||
const { extractParamsFromRequest, handleErrorResponse, handleResultResponse } = require('../../helpers/controller.helper');
|
||||
|
||||
function buildContext(req, res) {
|
||||
return {
|
||||
user: req.user
|
||||
}
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
MODULE_NAME: 'default'
|
||||
}
|
||||
|
||||
const generateControllers = (service, extraControllers = {}, options = defaultOptions) => {
|
||||
const defaultControllers = {
|
||||
find: async (req, res, next) => {
|
||||
const params = extractParamsFromRequest(req, res);
|
||||
|
||||
try {
|
||||
const result = await service.fetchAll(params, buildContext(req, res));
|
||||
return handleResultResponse(result.rows, result.count, params, res);
|
||||
} catch (error) {
|
||||
handleErrorResponse(options.MODULE_NAME, 'find', error, res)
|
||||
}
|
||||
},
|
||||
|
||||
findOne: async (req, res, next) => {
|
||||
const params = extractParamsFromRequest(req, res);
|
||||
|
||||
try {
|
||||
const result = await service.fetchOne(params, buildContext(req, res));
|
||||
console.log(result);
|
||||
return handleResultResponse(result, null, params, res, (result === null) ? httpStatus.NOT_FOUND : httpStatus.OK);
|
||||
} catch (error) {
|
||||
handleErrorResponse(options.MODULE_NAME, 'findOne', error, res)
|
||||
}
|
||||
},
|
||||
|
||||
count: async(req, res, next) => {
|
||||
const params = extractParamsFromRequest(req, res);
|
||||
|
||||
try {
|
||||
const result = await service.count(params, buildContext(req, res));
|
||||
return handleResultResponse(result, null, params, res);
|
||||
} catch (error) {
|
||||
handleErrorResponse(options.MODULE_NAME, 'count', error, res)
|
||||
}
|
||||
},
|
||||
|
||||
create: async (req, res, next) => {
|
||||
try {
|
||||
const result = await service.create(req.body, buildContext(req, res));
|
||||
return handleResultResponse(result, null, null, res, httpStatus.CREATED)
|
||||
} catch (error) {
|
||||
handleErrorResponse(options.MODULE_NAME, 'create', error, res)
|
||||
}
|
||||
},
|
||||
|
||||
update: async (req, res, next) => {
|
||||
try {
|
||||
const result = await service.update(req.params, req.body, buildContext(req, res));
|
||||
return handleResultResponse(result, null, req.params, res, httpStatus.OK)
|
||||
} catch (error) {
|
||||
handleErrorResponse(options.MODULE_NAME, 'update', error, res)
|
||||
}
|
||||
},
|
||||
|
||||
delete: async (req, res, next) => {
|
||||
try {
|
||||
const result = await service.delete(req.params, buildContext(req, res));
|
||||
return handleResultResponse(result, null, req.params, res, httpStatus.NO_CONTENT);
|
||||
} catch (error) {
|
||||
handleErrorResponse(options.MODULE_NAME, 'delete', error, res)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
//const associationControllers = generateAssociationControllers(model, options);
|
||||
|
||||
//console.log(associationControllers);
|
||||
|
||||
return {
|
||||
...defaultControllers,
|
||||
//...associationControllers
|
||||
...extraControllers
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = generateControllers;
|
||||
@ -10,10 +10,10 @@ const cors = require('cors');
|
||||
const helmet = require('helmet');
|
||||
const passport = require('passport');
|
||||
|
||||
const config = require('../config');
|
||||
const router = require('./router');
|
||||
const error = require('../middlewares/error');
|
||||
|
||||
|
||||
/**
|
||||
* Express instance
|
||||
* @public
|
||||
@ -75,7 +75,6 @@ app.use(cors({
|
||||
app.use(passport.initialize());
|
||||
require('./passport');
|
||||
|
||||
|
||||
// Set routes
|
||||
app.use('/api', router());
|
||||
|
||||
|
||||
@ -5,24 +5,8 @@ const express = require('./express');
|
||||
const logger = require('./logger');
|
||||
const models = require('./models');
|
||||
|
||||
//const modules = require('./modules');
|
||||
|
||||
//const middlewares = require('./middlewares');
|
||||
//const hooks = require('./hooks');
|
||||
//const plugins = require('./plugins');
|
||||
// const admin = require('./admin');
|
||||
//const store = require('./store');
|
||||
|
||||
module.exports = {
|
||||
//nestedConfigurations: nested,
|
||||
express,
|
||||
logger,
|
||||
//appConfigurations: app,
|
||||
//modules,
|
||||
models,
|
||||
//middlewares,
|
||||
//hooks,
|
||||
//plugins,
|
||||
// admin,
|
||||
//store
|
||||
};
|
||||
@ -3,7 +3,6 @@ const passport = require('passport');
|
||||
const { Strategy: LocalStrategy } = require('passport-local');
|
||||
const { Strategy: JWTStrategy} = require('passport-jwt');
|
||||
|
||||
const config = require('../config');
|
||||
const models = require('./models');
|
||||
const securityHelper = require('../helpers/security.helper');
|
||||
|
||||
@ -28,7 +27,9 @@ const localOptions = {
|
||||
|
||||
passport.use('local', new LocalStrategy(localOptions, async (email, password, done) => {
|
||||
try {
|
||||
const user = await models.User.findOne({ where: { email } });
|
||||
const user = await models.User.findOne({
|
||||
where: { email },
|
||||
});
|
||||
|
||||
if (_.isNull(user)) {
|
||||
return done(null, false, { message: 'User not found' })
|
||||
@ -38,6 +39,7 @@ passport.use('local', new LocalStrategy(localOptions, async (email, password, do
|
||||
if (!isPasswordValid) {
|
||||
return done(null, false, { message: 'Wrong Password' })
|
||||
} else {
|
||||
delete user.password;
|
||||
return done(null, user, { message: 'Logged in Successfully' });
|
||||
}
|
||||
}
|
||||
@ -49,7 +51,11 @@ passport.use('local', new LocalStrategy(localOptions, async (email, password, do
|
||||
// JWT
|
||||
passport.use('jwt', new JWTStrategy(securityHelper.jwtOptions, async (jwtPayload, done) => {
|
||||
try {
|
||||
const user = await models.User.findOne({ where: { id: jwtPayload.id } });
|
||||
const user = await models.User.findOne({
|
||||
attributes: { exclude: [ 'password' ] },
|
||||
where: { id: jwtPayload.id },
|
||||
raw: true
|
||||
});
|
||||
|
||||
if (_.isNull(user)) {
|
||||
return done(null, false, { message: 'User not found' })
|
||||
|
||||
@ -33,22 +33,95 @@ function buildErrorResponse(nameController, nameMethod, error) {
|
||||
return jsonResultFailed;
|
||||
}
|
||||
|
||||
function getTotalCount(result) {
|
||||
|
||||
const toType = function (obj) {
|
||||
return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
|
||||
}
|
||||
|
||||
switch (toType(result)) {
|
||||
case 'boolean':
|
||||
return 1;
|
||||
case 'object':
|
||||
return 1;
|
||||
case 'array':
|
||||
return result.length;
|
||||
case 'null':
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function setPaginationInfo(totalCount, res) {
|
||||
res.set({
|
||||
'X-Total-Count': totalCount,
|
||||
});
|
||||
|
||||
const params = extractParamsFromRequest(null, res);
|
||||
console.log(params);
|
||||
if (params.paginate) {
|
||||
const
|
||||
page = (params.paginate && params.paginate.page) ? params.paginate.page : null,
|
||||
limit = (params.paginate && params.paginate.limit) ? params.paginate.limit : null,
|
||||
count = (limit) ? Math.ceil(totalCount / limit) : null;
|
||||
|
||||
if (params.paginate.hasNextPages(count)) {
|
||||
const nextPage = params.paginate.href();
|
||||
res.set('Link', nextPage + '; rel=next');
|
||||
res.set('Pagination-Next-Page', true);
|
||||
} else {
|
||||
res.set('Pagination-Next-Page', false);
|
||||
}
|
||||
|
||||
res.set({
|
||||
'Pagination-Count': count,
|
||||
'Pagination-Page': page,
|
||||
'Pagination-Limit': limit,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// PUBLIC FUNCTIONS
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function extractParamsFromRequest(req, res) {
|
||||
const result = {};
|
||||
|
||||
result.params = (req && req.params) ? req.params : null;
|
||||
result.query = (req && req.query) ? req.params : null;
|
||||
|
||||
if (res && res.locals) {
|
||||
Object.keys(res.locals).forEach(key => {
|
||||
result[key] = res.locals[key]
|
||||
})
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function handleErrorResponse(controllerName, methodName, error, res) {
|
||||
|
||||
const jsonResultFailed = buildErrorResponse(controllerName, methodName, error);
|
||||
res.status(httpStatus.INTERNAL_SERVER_ERROR).send(jsonResultFailed);
|
||||
}
|
||||
|
||||
function handleResultResponse(result, totalCount = null, params, res, statusCode = httpStatus.OK) {
|
||||
setPaginationInfo((totalCount) ? totalCount : getTotalCount(result), res);
|
||||
res.status(statusCode).send(result);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// MODULE EXPORTS
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module.exports = {
|
||||
extractParamsFromRequest,
|
||||
handleErrorResponse,
|
||||
handleResultResponse,
|
||||
// for testing
|
||||
buildErrorLog,
|
||||
buildErrorResponse
|
||||
|
||||
19
helpers/middleware.helper.js
Normal file
19
helpers/middleware.helper.js
Normal file
@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
function compose(middlewareArray) {
|
||||
if (!middlewareArray.length) {
|
||||
return function (_req, _res, next) { next(); };
|
||||
}
|
||||
|
||||
let head = middlewareArray[0];
|
||||
let tail = middlewareArray.slice(1);
|
||||
|
||||
return function (req, res, next) {
|
||||
head(req, res, function (err) {
|
||||
if (err) return next(err);
|
||||
compose(tail)(req, res, next);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = compose;
|
||||
206
helpers/service.helper.js
Normal file
206
helpers/service.helper.js
Normal file
@ -0,0 +1,206 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
const parseParamsToFindOptions = (params) => {
|
||||
const result = {};
|
||||
|
||||
// Query
|
||||
if (params.query) {
|
||||
result.query = params.query
|
||||
}
|
||||
|
||||
// Where
|
||||
if (params.params) {
|
||||
result.where = params.params
|
||||
}
|
||||
|
||||
// Paginate
|
||||
if (params.paginate) {
|
||||
result.offset = params.paginate.limit * (params.paginate.page - 1);
|
||||
result.limit = result.offset + params.paginate.limit;
|
||||
}
|
||||
|
||||
// Order
|
||||
if (params.sort) {
|
||||
result.order = [];
|
||||
Object.keys(params.sort).forEach(key => {
|
||||
let dir = params.sort[key] ? 'ASC' : 'DESC';
|
||||
result.order.push([key, dir])
|
||||
});
|
||||
}
|
||||
|
||||
// Attributes
|
||||
if (params.fields) {
|
||||
if (params.fields.validFields) {
|
||||
result.attributes = params.fields.validFields
|
||||
}
|
||||
if (params.fields.invalidFields && Array.isArray(params.fields.invalidFields)) {
|
||||
result.attributes = {
|
||||
...result.attributes,
|
||||
exclude: params.fields.invalidFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const defaultOptions = {};
|
||||
|
||||
const generateService = (model, extraMethods = {}, options = defaultOptions) => {
|
||||
const defaultService = {
|
||||
|
||||
fetchAll: async (params, context) => {
|
||||
const findOptions = parseParamsToFindOptions(params);
|
||||
return await model.findAndCountAll(findOptions);
|
||||
},
|
||||
|
||||
fetchOne: async (params, context) => {
|
||||
return await model.findOne({ where: { 'id': params.id } });
|
||||
},
|
||||
|
||||
count: async (params, context) => {
|
||||
// Convert `params` object to filters compatible with Bookshelf.
|
||||
//const filters = strapi.utils.models.convertParams('post', params);
|
||||
|
||||
return await model.count(params);
|
||||
},
|
||||
|
||||
create: async (values, context) => {
|
||||
return await model.create(values);
|
||||
},
|
||||
|
||||
update: async (params, values, context) => {
|
||||
// Extract values related to relational data.
|
||||
const relations = _.pick(values, Post.associations.map(ast => ast.alias));
|
||||
const data = _.omit(values, Post.associations.map(ast => ast.alias));
|
||||
|
||||
// Create entry with no-relational data.
|
||||
const entry = await Post.forge(params).save(data);
|
||||
|
||||
// Create relational data and return the entry.
|
||||
return Post.updateRelations(Object.assign(params, {
|
||||
values: relations
|
||||
}));
|
||||
},
|
||||
|
||||
delete: async (params, context) => {
|
||||
const numAffectedRows = await model.destroy({ where: { 'id': params.id } });
|
||||
return (numAffectedRows > 0);
|
||||
|
||||
/*params.values = {};
|
||||
Post.associations.map(association => {
|
||||
switch (association.nature) {
|
||||
case 'oneWay':
|
||||
case 'oneToOne':
|
||||
case 'manyToOne':
|
||||
case 'oneToManyMorph':
|
||||
params.values[association.alias] = null;
|
||||
break;
|
||||
case 'oneToMany':
|
||||
case 'manyToMany':
|
||||
case 'manyToManyMorph':
|
||||
params.values[association.alias] = [];
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
||||
await Post.updateRelations(params);
|
||||
|
||||
return Post.forge(params).destroy();*/
|
||||
},
|
||||
|
||||
search: async (params, context) => {
|
||||
// Convert `params` object to filters compatible with Bookshelf.
|
||||
const filters = strapi.utils.models.convertParams('post', params);
|
||||
// Select field to populate.
|
||||
const populate = Post.associations
|
||||
.filter(ast => ast.autoPopulate !== false)
|
||||
.map(ast => ast.alias);
|
||||
|
||||
const associations = Post.associations.map(x => x.alias);
|
||||
const searchText = Object.keys(Post._attributes)
|
||||
.filter(attribute => attribute !== Post.primaryKey && !associations.includes(attribute))
|
||||
.filter(attribute => ['string', 'text'].includes(Post._attributes[attribute].type));
|
||||
|
||||
const searchNoText = Object.keys(Post._attributes)
|
||||
.filter(attribute => attribute !== Post.primaryKey && !associations.includes(attribute))
|
||||
.filter(attribute => !['string', 'text', 'boolean', 'integer', 'decimal', 'float'].includes(Post._attributes[attribute].type));
|
||||
|
||||
const searchInt = Object.keys(Post._attributes)
|
||||
.filter(attribute => attribute !== Post.primaryKey && !associations.includes(attribute))
|
||||
.filter(attribute => ['integer', 'decimal', 'float'].includes(Post._attributes[attribute].type));
|
||||
|
||||
const searchBool = Object.keys(Post._attributes)
|
||||
.filter(attribute => attribute !== Post.primaryKey && !associations.includes(attribute))
|
||||
.filter(attribute => ['boolean'].includes(Post._attributes[attribute].type));
|
||||
|
||||
const query = (params._q || '').replace(/[^a-zA-Z0-9.-\s]+/g, '');
|
||||
|
||||
return Post.query(qb => {
|
||||
// Search in columns which are not text value.
|
||||
searchNoText.forEach(attribute => {
|
||||
qb.orWhereRaw(`LOWER(${attribute}) LIKE '%${_.toLower(query)}%'`);
|
||||
});
|
||||
|
||||
if (!_.isNaN(_.toNumber(query))) {
|
||||
searchInt.forEach(attribute => {
|
||||
qb.orWhereRaw(`${attribute} = ${_.toNumber(query)}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (query === 'true' || query === 'false') {
|
||||
searchBool.forEach(attribute => {
|
||||
qb.orWhereRaw(`${attribute} = ${_.toNumber(query === 'true')}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Search in columns with text using index.
|
||||
switch (Post.client) {
|
||||
case 'mysql':
|
||||
qb.orWhereRaw(`MATCH(${searchText.join(',')}) AGAINST(? IN BOOLEAN MODE)`, `*${query}*`);
|
||||
break;
|
||||
case 'pg':
|
||||
{
|
||||
const searchQuery = searchText.map(attribute =>
|
||||
_.toLower(attribute) === attribute ?
|
||||
`to_tsvector(${attribute})` :
|
||||
`to_tsvector('${attribute}')`
|
||||
);
|
||||
|
||||
qb.orWhereRaw(`${searchQuery.join(' || ')} @@ to_tsquery(?)`, query);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.sort) {
|
||||
qb.orderBy(filters.sort.key, filters.sort.order);
|
||||
}
|
||||
|
||||
if (filters.skip) {
|
||||
qb.offset(_.toNumber(filters.skip));
|
||||
}
|
||||
|
||||
if (filters.limit) {
|
||||
qb.limit(_.toNumber(filters.limit));
|
||||
}
|
||||
}).fetchAll({
|
||||
withRelated: populate
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...defaultService,
|
||||
//...associationControllers
|
||||
...extraMethods
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
generateService,
|
||||
parseParamsToFindOptions,
|
||||
defaultOptions
|
||||
}
|
||||
@ -1,19 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
const passport = require('passport');
|
||||
const httpStatus = require('http-status');
|
||||
const compose = require('../helpers/middleware.helper');
|
||||
|
||||
exports.isRegisteresUser = passport.authenticate('local', { session: false });
|
||||
exports.isLoggedUser = passport.authenticate('jwt', { session: false });
|
||||
|
||||
/**
|
||||
* Authorization Required middleware.
|
||||
*/
|
||||
exports.isAuthorized = (req, res, next) => {
|
||||
const provider = req.path.split('/').slice(-1)[0];
|
||||
const token = req.user.tokens.find(token => token.kind === provider);
|
||||
if (token) {
|
||||
next();
|
||||
} else {
|
||||
res.redirect(`/auth/${provider}`);
|
||||
const isRegisteredUser = passport.authenticate('local', { session: false });
|
||||
const isLoggedUser = passport.authenticate('jwt', { session: false });
|
||||
const isAdministratorUser = compose([isLoggedUser,
|
||||
(req, res, next) => {
|
||||
const user = req.user;
|
||||
if (user.role >= 8) {
|
||||
next();
|
||||
} else {
|
||||
return res.status(httpStatus.UNAUTHORIZED).send('UNAUTHORIZED');
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
module.exports = {
|
||||
isRegisteredUser,
|
||||
isLoggedUser,
|
||||
isAdministratorUser
|
||||
};
|
||||
66
middlewares/fields.js
Normal file
66
middlewares/fields.js
Normal file
@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Fields middleware
|
||||
This module exports a function that takes an optional configuration object parameter with the following
|
||||
properties:
|
||||
{
|
||||
validFields: Array of strings (case sensitive),
|
||||
invalidFields: Array of strings (case sensitive),
|
||||
}
|
||||
and returns an express compatible middleware function (req, res, next) that parses the following
|
||||
parameters from the req.query object and sets an object on the res.locals.fields having the following
|
||||
properties:
|
||||
|
||||
Object - with fields for the attributes value fields
|
||||
|
||||
*/
|
||||
|
||||
const middleware = (config) => {
|
||||
|
||||
config = config || {
|
||||
validFields: null,
|
||||
invalidFields: null,
|
||||
};
|
||||
|
||||
if (config.validFields && (!Array.isArray(config.validFields))) {
|
||||
throw new Error("config.validFields should be a non empty array of strings");
|
||||
}
|
||||
|
||||
if (config.invalidFields && (!Array.isArray(config.invalidFields))) {
|
||||
throw new Error("config.invalidFields should be a non empty array of strings");
|
||||
}
|
||||
|
||||
return function (req, res, next) {
|
||||
const fieldNames = Array.isArray(req.query.fields)
|
||||
? req.query.fields
|
||||
: req.query.fields
|
||||
? [req.query.fields]
|
||||
: [];
|
||||
|
||||
const fieldObject = fieldNames.filter(field => {
|
||||
let isOk = true;
|
||||
if (config.validFields) {
|
||||
isOk = config.validFields.includes(field)
|
||||
}
|
||||
|
||||
if (config.invalidFields) {
|
||||
isOk = !config.invalidFields.includes(field)
|
||||
}
|
||||
|
||||
return isOk;
|
||||
});
|
||||
|
||||
res.locals.fields = {};
|
||||
|
||||
(Object.keys(fieldObject).length > 0) ? res.locals.fields['fields'] = fieldObject : null;
|
||||
config.invalidFields ? res.locals.fields['invalidFields'] = config.invalidFields : null;
|
||||
config.validFields ? res.locals.fields['validFields'] = config.validFields : null,
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
middleware
|
||||
}
|
||||
122
middlewares/paginate.js
Normal file
122
middlewares/paginate.js
Normal file
@ -0,0 +1,122 @@
|
||||
'use strict';
|
||||
|
||||
// Node.js pagination middleware and view helpers.
|
||||
|
||||
// * Author: [@niftylettuce](https://twitter.com/#!/niftylettuce)
|
||||
// * Source: <https://github.com/niftylettuce/middleware paginate>
|
||||
|
||||
const qs = require('qs');
|
||||
const url = require('url');
|
||||
const _ = require('lodash');
|
||||
//const util = require('util');
|
||||
|
||||
const href = (req) => {
|
||||
|
||||
return function (prev, params) {
|
||||
const query = _.clone(req.query);
|
||||
|
||||
if (typeof prev === 'object') {
|
||||
params = prev;
|
||||
prev = false;
|
||||
} else {
|
||||
prev = (typeof prev === 'boolean') ? prev : false;
|
||||
query.page = parseInt(query.page, 10);
|
||||
query.page = prev ? query.page -= 1 : query.page += 1;
|
||||
query.page = (query.page < 1) ? 1 : query.page;
|
||||
}
|
||||
|
||||
// allow overriding querystring params
|
||||
// (useful for sorting and filtering)
|
||||
// another alias for `_.assign` is `_.extend`
|
||||
if (_.isObject(params))
|
||||
query = _.assign(query, params);
|
||||
|
||||
return url.parse(req.originalUrl).pathname + '?' + qs.stringify(query);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const hasNextPages = (req) => {
|
||||
return function (pageCount) {
|
||||
if (typeof pageCount !== 'number' || pageCount < 0)
|
||||
throw new Error('middleware paginate: `pageCount` is not a number >= 0');
|
||||
return req.query.page < pageCount;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const getArrayPages = (req) => {
|
||||
return function (limit, pageCount, currentPage) {
|
||||
const maxPage = pageCount;
|
||||
|
||||
// limit default is 3
|
||||
limit = limit || 3;
|
||||
|
||||
if (typeof limit !== 'number' || limit < 0)
|
||||
throw new Error('middleware paginate: `limit` is not a number >= 0');
|
||||
|
||||
if (typeof pageCount !== 'number' || pageCount < 0)
|
||||
throw new Error('middleware paginate: `pageCount` is not a number >= 0');
|
||||
|
||||
currentPage = parseInt(currentPage, 10);
|
||||
if (Number.isNaN(currentPage) || currentPage < 0)
|
||||
throw new Error('middleware paginate: `currentPage` is not a number >= 0');
|
||||
|
||||
if (limit > 0) {
|
||||
let end = Math.min(Math.max(currentPage + Math.floor(limit / 2), limit), pageCount);
|
||||
let start = Math.max(1, (currentPage < (limit - 1)) ? 1 : (end - limit) + 1);
|
||||
|
||||
let pages = [];
|
||||
for (let i = start; i <= end; i++) {
|
||||
pages.push({
|
||||
number: i,
|
||||
url: href(req)()
|
||||
.replace('page=' + (currentPage + 1), 'page=' + i)
|
||||
});
|
||||
}
|
||||
|
||||
return pages;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const middleware = (limit = 10, maxLimit = 50) => {
|
||||
|
||||
const _limit = (typeof limit === 'number') ? parseInt(limit, 10) : 10;
|
||||
const _maxLimit = (typeof maxLimit === 'number') ? parseInt(maxLimit, 10) : 50;
|
||||
|
||||
return function _middleware(req, res, next) {
|
||||
req.query.page = (typeof req.query.page === 'string') ? parseInt(req.query.page, 10) || 1 : 1;
|
||||
req.query.limit = (typeof req.query.limit === 'string') ? parseInt(req.query.limit, 10) || 0 : _limit;
|
||||
|
||||
if (req.query.limit > _maxLimit)
|
||||
req.query.limit = _maxLimit;
|
||||
|
||||
if (req.query.page < 1)
|
||||
req.query.page = 1;
|
||||
|
||||
if (req.query.limit < 0)
|
||||
req.query.limit = 0;
|
||||
|
||||
//req.skip = req.offset = (req.query.page * req.query.limit) - req.query.limit;
|
||||
|
||||
res.locals.paginate = res.locals.paginate ? res.locals.paginate : {};
|
||||
res.locals.paginate.page = req.query.page;
|
||||
res.locals.paginate.limit = req.query.limit;
|
||||
res.locals.paginate.href = href(req);
|
||||
res.locals.paginate.hasPreviousPages = req.query.page > 1;
|
||||
res.locals.paginate.hasNextPages = hasNextPages(req);
|
||||
res.locals.paginate.getArrayPages = getArrayPages(req);
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
href,
|
||||
hasNextPages,
|
||||
getArrayPages,
|
||||
middleware
|
||||
}
|
||||
@ -28,7 +28,7 @@ module.exports = (schema, useJoiError = false) => {
|
||||
const _schema = schema
|
||||
|
||||
if (_schema) {
|
||||
console.log(req.body);
|
||||
|
||||
// Validate req.body using the schema and validation options
|
||||
return Joi.validate(req.body, _schema, _validationOptions, (err, data) => {
|
||||
|
||||
|
||||
67
middlewares/sort.js
Normal file
67
middlewares/sort.js
Normal file
@ -0,0 +1,67 @@
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
Sort middleware
|
||||
This module exports a function that takes a configuration object parameter with the following
|
||||
properties:
|
||||
{
|
||||
validKeys: Array of strings (case sensitive),
|
||||
default: String (optional, by default assigned to the first element of the
|
||||
validKeys array with ascending sorting order unless prefixed by a "-")
|
||||
}
|
||||
and returns an express compatible middleware function (req, res, next) that parses the following
|
||||
parameters from the req.query object and sets an object on the res.locals.sort having the following
|
||||
properties:
|
||||
|
||||
Object - with keys for the sort value fields and value a boolean denoting ascending order
|
||||
or not
|
||||
|
||||
*/
|
||||
|
||||
const middleware = (config) => {
|
||||
if (typeof config !== "object" || config === null) {
|
||||
throw new Error("The config parameter is mandatory and should be an object!");
|
||||
}
|
||||
|
||||
config = config || {};
|
||||
if (!config.default && (!Array.isArray(config.validKeys) || config.validKeys.length === 0)) {
|
||||
throw new Error("config.validKeys should be a non empty array of strings or a config.default key should be defined!");
|
||||
}
|
||||
|
||||
config.default = config.default || config.validKeys[0];
|
||||
|
||||
const defaultSortIsAscending = config.default.substring(0, 1) !== "-";
|
||||
|
||||
return function (req, res, next) {
|
||||
const sortKeys = Array.isArray(req.query.sort)
|
||||
? req.query.sort
|
||||
: req.query.sort
|
||||
? [req.query.sort]
|
||||
: [];
|
||||
|
||||
const sortObject = sortKeys.reduce(function (c, key) {
|
||||
const ascending = key.substring(0, 1) !== "-";
|
||||
if (!ascending) {
|
||||
key = key.substr(1);
|
||||
}
|
||||
if (key && config.validKeys && config.validKeys.indexOf(key) !== -1 ) {
|
||||
c[key] = ascending;
|
||||
}
|
||||
return c;
|
||||
}, {});
|
||||
|
||||
if (Object.keys(sortObject).length === 0) {
|
||||
sortObject[
|
||||
defaultSortIsAscending
|
||||
? config.default
|
||||
: config.default.substr(1)
|
||||
] = defaultSortIsAscending;
|
||||
}
|
||||
res.locals.sort = sortObject;
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
middleware
|
||||
}
|
||||
@ -2,11 +2,9 @@
|
||||
|
||||
const _ = require('lodash');
|
||||
const httpStatus = require('http-status');
|
||||
const passport = require('passport');
|
||||
const controllerHelper = require('../../helpers/controller.helper');
|
||||
const messageHelper = require('../../helpers/message.helper');
|
||||
const securityHelper = require('../../helpers/security.helper');
|
||||
const authService = require('./auth.service');
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -13,7 +13,7 @@ const AccessValidator = require('../../middlewares/accessValidator');
|
||||
|
||||
routes.post('/auth',
|
||||
SchemaValidator(authValidation.LoginInputType, true),
|
||||
AccessValidator.isRegisteresUser,
|
||||
AccessValidator.isRegisteredUser,
|
||||
authController.login,
|
||||
);
|
||||
|
||||
|
||||
@ -19,6 +19,11 @@ module.exports = function (sequelize, DataTypes) {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
role: {
|
||||
type: DataTypes.TINYINT,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
},
|
||||
}, {
|
||||
tableName: 'user',
|
||||
freezeTableName: true,
|
||||
|
||||
21
modules/blog/blog.controller.js
Normal file
21
modules/blog/blog.controller.js
Normal file
@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const generateControllers = require('../../core/controllers');
|
||||
const blogService = require('./blog.service');
|
||||
|
||||
|
||||
// Module Name
|
||||
const MODULE_NAME = '[blog.controller]';
|
||||
|
||||
// Error Messages
|
||||
//const NOT_FOUND = 'Videogame not found';
|
||||
|
||||
// Success Messages
|
||||
//const VG_CT_VIDEOGAME_DELETED_SUCCESSFULLY = 'Videogame deleted successfully';
|
||||
|
||||
|
||||
const controllerOptions = { MODULE_NAME };
|
||||
const extraControllers = {};
|
||||
|
||||
module.exports = generateControllers(blogService, extraControllers, controllerOptions);
|
||||
|
||||
@ -1,35 +1,37 @@
|
||||
const routes = require('express').Router();
|
||||
const models = require('../../core/models');
|
||||
//const postService = require('./post.service')(models.Post);
|
||||
//const postController = require('./post.controller')(postService);
|
||||
|
||||
const { ModelHandler } = require('sequelize-handlers');
|
||||
const postHandler = new ModelHandler(models.Post);
|
||||
/*const postService = require('./post.service')(models.Post);
|
||||
const postController = require('./post.controller')(postService);*/
|
||||
|
||||
routes.use((req, res, next) => {
|
||||
// here we can access the req.params object and make auth checks
|
||||
next();
|
||||
});
|
||||
const { isAdministratorUser, isLoggedUser } = require('../../middlewares/accessValidator');
|
||||
const PaginateMiddleware = require('../../middlewares/paginate');
|
||||
const SortMiddleware = require('../../middlewares/sort');
|
||||
const FieldMiddleware = require('../../middlewares/fields');
|
||||
|
||||
routes.get('/posts', postHandler.query());
|
||||
const blogController = require('./blog.controller')
|
||||
|
||||
/*routes.get('/posts', function (req, res) {
|
||||
postController.find(req).then(data => {
|
||||
console.log(data);
|
||||
res.status(200).json(data);
|
||||
})
|
||||
});*/
|
||||
|
||||
routes.get('/posts/count', function (req, res) {
|
||||
//res.status(200).json(postController.count(req));
|
||||
});
|
||||
|
||||
routes.get('/posts/:id', function (req, res) {
|
||||
//res.status(200).json(postController.findOne(req));
|
||||
});
|
||||
routes.get('/posts',
|
||||
isLoggedUser,
|
||||
FieldMiddleware.middleware({
|
||||
invalidFields: ['user', 'created']
|
||||
}),
|
||||
PaginateMiddleware.middleware(),
|
||||
SortMiddleware.middleware({ default: "date" }),
|
||||
blogController.find);
|
||||
|
||||
routes.post('/posts', isAdministratorUser, blogController.create);
|
||||
|
||||
routes.get('/posts/:id',
|
||||
isLoggedUser,
|
||||
FieldMiddleware.middleware({
|
||||
invalidFields: ['updatedAt', 'createdAt']
|
||||
}),
|
||||
//PaginateMiddleware.middleware(),
|
||||
//SortMiddleware.middleware({ default: "date" }),
|
||||
blogController.findOne);
|
||||
|
||||
|
||||
routes.put('/posts/:id', isAdministratorUser, blogController.update);
|
||||
routes.delete('/posts/:id', isAdministratorUser, blogController.delete);
|
||||
|
||||
module.exports = routes;
|
||||
23
modules/blog/blog.service.js
Normal file
23
modules/blog/blog.service.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* global Post */
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const { generateService, parseParamsToFindOptions } = require('../../helpers/service.helper');
|
||||
const models = require('../../core/models');
|
||||
|
||||
const extraMethods = {
|
||||
fetchOne: async (params, context) => {
|
||||
const findOptions = parseParamsToFindOptions(params);
|
||||
console.log(findOptions);
|
||||
const result = await models.Post.findByPk(findOptions.query.id, {
|
||||
include: [{ all: true }]
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
return result;
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = generateService(models.Post, extraMethods);
|
||||
14
modules/blog/category.controller.js
Normal file
14
modules/blog/category.controller.js
Normal file
@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const generateControllers = require('../../core/controllers');
|
||||
const categoryService = require('./category.service');
|
||||
|
||||
|
||||
// Module Name
|
||||
const MODULE_NAME = '[category.controller]';
|
||||
|
||||
const controllerOptions = { MODULE_NAME };
|
||||
const extraControllers = {};
|
||||
|
||||
module.exports = generateControllers(categoryService, extraControllers, controllerOptions);
|
||||
|
||||
@ -2,6 +2,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
const Category = sequelize.define('Category', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true
|
||||
},
|
||||
name: {
|
||||
@ -11,7 +12,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
sort: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: 0,
|
||||
allowNull: false,
|
||||
allowNull: false,
|
||||
}
|
||||
}, {
|
||||
tableName: 'category',
|
||||
@ -20,7 +21,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||
});
|
||||
|
||||
Category.beforeCreate((category) => {
|
||||
category.dataValues.id = Math.floor(Math.random() * (999 - 8)) + 8;
|
||||
//category.dataValues.id = Math.floor(Math.random() * (999 - 8)) + 8;
|
||||
})
|
||||
|
||||
Category.associate = function (models) {
|
||||
|
||||
17
modules/blog/category.routes.js
Normal file
17
modules/blog/category.routes.js
Normal file
@ -0,0 +1,17 @@
|
||||
const routes = require('express').Router();
|
||||
|
||||
const { isAdministratorUser, isLoggedUser } = require('../../middlewares/accessValidator');
|
||||
/*const PaginateMiddleware = require('../../middlewares/paginate');
|
||||
const SortMiddleware = require('../../middlewares/sort');
|
||||
const FieldMiddleware = require('../../middlewares/fields');*/
|
||||
|
||||
const categoryController = require('./category.controller');
|
||||
|
||||
routes.get('/categories', isLoggedUser, categoryController.find);
|
||||
routes.get('/categories/:id', isLoggedUser, categoryController.findOne);
|
||||
|
||||
routes.post('/categories/', isAdministratorUser, categoryController.create);
|
||||
routes.put('/categories/:id', isAdministratorUser, categoryController.update);
|
||||
routes.delete('/categories/:id', isAdministratorUser, categoryController.delete);
|
||||
|
||||
module.exports = routes;
|
||||
10
modules/blog/category.service.js
Normal file
10
modules/blog/category.service.js
Normal file
@ -0,0 +1,10 @@
|
||||
/* global Post */
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const { generateService, parseParamsToFindOptions } = require('../../helpers/service.helper');
|
||||
const models = require('../../core/models');
|
||||
|
||||
const extraMethods = {};
|
||||
|
||||
module.exports = generateService(models.Category, extraMethods);
|
||||
@ -49,7 +49,6 @@
|
||||
"pino": "^4.7.1",
|
||||
"response-time": "^2.3.2",
|
||||
"sequelize": "^5.3.5",
|
||||
"sequelize-handlers": "^1.0.7",
|
||||
"vm": "^0.1.0",
|
||||
"winston": "^3.2.1"
|
||||
}
|
||||
|
||||
@ -2828,13 +2828,6 @@ send@0.16.2:
|
||||
range-parser "~1.2.0"
|
||||
statuses "~1.4.0"
|
||||
|
||||
sequelize-handlers@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/sequelize-handlers/-/sequelize-handlers-1.0.7.tgz#1ef1d81447bdcd608ae42f5855d7fc2d978bf57a"
|
||||
integrity sha512-fyNPYLSF03mRwy/gyJJOxH7fN3R6os5Ptsj18Gezfpt/T6mLFwOvxHp+dWd6O1KDNBr0JZi4kIj8FuDq55upbw==
|
||||
dependencies:
|
||||
lodash "^4.17.11"
|
||||
|
||||
sequelize-pool@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-1.0.2.tgz#89c767882bbdb8a41dac66922ed9820939a5401e"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user