..
This commit is contained in:
parent
94156bed0e
commit
81d214b50a
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ node_modules
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
database: {
|
database: {
|
||||||
username: 'acana_wms',
|
username: 'lqdvi',
|
||||||
password: 'a85*MukC45.',
|
password: 'Z286y386*a',
|
||||||
database: 'acana_wms',
|
database: 'lqdvi_v2',
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
dialect: 'mysql'
|
dialect: 'mysql',
|
||||||
},
|
},
|
||||||
|
|
||||||
session: {
|
session: {
|
||||||
|
|||||||
@ -1,58 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
// Dependencies.
|
|
||||||
const glob = require('glob');
|
|
||||||
const { get, upperFirst, camelCase } = require('lodash');
|
|
||||||
const utils = require('../utils');
|
|
||||||
const models = require('./models');
|
|
||||||
|
|
||||||
module.exports.nested = function() {
|
|
||||||
return Promise.all([
|
|
||||||
|
|
||||||
// Load root configurations.
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
glob('./config/index.js', {
|
|
||||||
cwd: this.config.appPath,
|
|
||||||
dot: true
|
|
||||||
}, (err, files) => {
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.loadConfig.call(this, files).then(resolve).catch(reject);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
module.exports.app = async function() {
|
|
||||||
|
|
||||||
// Set connections.
|
|
||||||
this.connections = {};
|
|
||||||
|
|
||||||
// Set current environment config.
|
|
||||||
this.config.currentEnvironment = this.config.environments[this.config.environment] || {};
|
|
||||||
|
|
||||||
// default settings
|
|
||||||
this.config.port = get(this.config.currentEnvironment, 'server.port') || this.config.port;
|
|
||||||
this.config.host = get(this.config.currentEnvironment, 'server.host') || this.config.host;
|
|
||||||
|
|
||||||
// Set current URL
|
|
||||||
this.config.url = getURLFromSegments({
|
|
||||||
hostname: this.config.host,
|
|
||||||
port: this.config.port
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set models
|
|
||||||
this.models = models.call(this);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const getURLFromSegments = function ({ hostname, port, ssl = false }) {
|
|
||||||
const protocol = ssl ? 'https' : 'http';
|
|
||||||
const defaultPort = ssl ? 443 : 80;
|
|
||||||
const portString = (port === undefined || parseInt(port) === defaultPort) ? '' : `:${port}`;
|
|
||||||
|
|
||||||
return `${protocol}://${hostname}${portString}`;
|
|
||||||
};
|
|
||||||
116
core/express.js
116
core/express.js
@ -14,78 +14,76 @@ const router = require('./router');
|
|||||||
const error = require('../middlewares/error');
|
const error = require('../middlewares/error');
|
||||||
const access = require('../middlewares/access');
|
const access = require('../middlewares/access');
|
||||||
|
|
||||||
module.exports = async function () {
|
/**
|
||||||
/**
|
* Express instance
|
||||||
* Express instance
|
* @public
|
||||||
* @public
|
*/
|
||||||
*/
|
const app = express();
|
||||||
const app = express();
|
|
||||||
|
|
||||||
// request logging. dev: console | production: file
|
// request logging. dev: console | production: file
|
||||||
//app.use(morgan(logs));
|
//app.use(morgan(logs));
|
||||||
|
|
||||||
// parse body params and attache them to req.body
|
// parse body params and attache them to req.body
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
app.use(bodyParser.urlencoded({
|
app.use(bodyParser.urlencoded({
|
||||||
extended: true
|
extended: true
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// set up the response-time middleware
|
// set up the response-time middleware
|
||||||
app.use(responseTime());
|
app.use(responseTime());
|
||||||
|
|
||||||
// gzip compression
|
// gzip compression
|
||||||
app.use(compress());
|
app.use(compress());
|
||||||
|
|
||||||
// lets you use HTTP verbs such as PUT or DELETE
|
// lets you use HTTP verbs such as PUT or DELETE
|
||||||
// in places where the client doesn't support it
|
// in places where the client doesn't support it
|
||||||
app.use(methodOverride());
|
app.use(methodOverride());
|
||||||
|
|
||||||
// secure apps by setting various HTTP headers
|
// secure apps by setting various HTTP headers
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
|
|
||||||
|
|
||||||
// enable CORS - Cross Origin Resource Sharing
|
// enable CORS - Cross Origin Resource Sharing
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
exposeHeaders: [
|
exposeHeaders: [
|
||||||
"WWW-Authenticate",
|
"WWW-Authenticate",
|
||||||
"Server-Authorization"
|
"Server-Authorization"
|
||||||
],
|
],
|
||||||
maxAge: 31536000,
|
maxAge: 31536000,
|
||||||
credentials: true,
|
credentials: true,
|
||||||
allowMethods: [
|
allowMethods: [
|
||||||
"GET",
|
"GET",
|
||||||
"POST",
|
"POST",
|
||||||
"PUT",
|
"PUT",
|
||||||
"PATCH",
|
"PATCH",
|
||||||
"DELETE",
|
"DELETE",
|
||||||
"OPTIONS",
|
"OPTIONS",
|
||||||
"HEAD"
|
"HEAD"
|
||||||
],
|
],
|
||||||
allowHeaders: [
|
allowHeaders: [
|
||||||
"Content-Type",
|
"Content-Type",
|
||||||
"Authorization",
|
"Authorization",
|
||||||
"X-Frame-Options",
|
"X-Frame-Options",
|
||||||
"Origin"
|
"Origin"
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
// Access validator
|
// Access validator
|
||||||
app.use(passport.initialize());
|
app.use(passport.initialize());
|
||||||
passport.use('jwt', access.jwt.call(this));
|
passport.use('jwt', access.jwt);
|
||||||
|
|
||||||
|
|
||||||
// Set routes
|
// Set routes
|
||||||
app.use('/api', await router.call(this));
|
app.use('/api', router());
|
||||||
|
|
||||||
// if error is not an instanceOf APIError, convert it.
|
// if error is not an instanceOf APIError, convert it.
|
||||||
app.use(error.converter);
|
app.use(error.converter);
|
||||||
|
|
||||||
// catch 404 and forward to error handler
|
// catch 404 and forward to error handler
|
||||||
app.use(error.notFound);
|
app.use(error.notFound);
|
||||||
|
|
||||||
// error handler, send stacktrace only during development
|
// error handler, send stacktrace only during development
|
||||||
app.use(error.handler);
|
app.use(error.handler);
|
||||||
|
|
||||||
return app;
|
module.exports = app;
|
||||||
}
|
|
||||||
@ -1,10 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { nested, app } = require('./configurations');
|
//const { nested, app } = require('./configurations');
|
||||||
const express = require('./express');
|
const express = require('./express');
|
||||||
const logger = require('./logger');
|
const logger = require('./logger');
|
||||||
const modules = require('./modules');
|
|
||||||
const models = require('./models');
|
const models = require('./models');
|
||||||
|
|
||||||
|
//const modules = require('./modules');
|
||||||
|
|
||||||
//const middlewares = require('./middlewares');
|
//const middlewares = require('./middlewares');
|
||||||
//const hooks = require('./hooks');
|
//const hooks = require('./hooks');
|
||||||
//const plugins = require('./plugins');
|
//const plugins = require('./plugins');
|
||||||
@ -12,11 +14,11 @@ const models = require('./models');
|
|||||||
//const store = require('./store');
|
//const store = require('./store');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
nestedConfigurations: nested,
|
//nestedConfigurations: nested,
|
||||||
express,
|
express,
|
||||||
logger,
|
logger,
|
||||||
appConfigurations: app,
|
//appConfigurations: app,
|
||||||
modules,
|
//modules,
|
||||||
models,
|
models,
|
||||||
//middlewares,
|
//middlewares,
|
||||||
//hooks,
|
//hooks,
|
||||||
|
|||||||
@ -2,6 +2,10 @@ const glob = require('glob');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Sequelize = require('sequelize');
|
const Sequelize = require('sequelize');
|
||||||
|
|
||||||
|
const config = require('../config');
|
||||||
|
const log = require('./logger');
|
||||||
|
|
||||||
|
|
||||||
const modulesDir = path.resolve(__dirname + '/../modules/')
|
const modulesDir = path.resolve(__dirname + '/../modules/')
|
||||||
const basename = path.basename(__dirname);
|
const basename = path.basename(__dirname);
|
||||||
const globOptions = {
|
const globOptions = {
|
||||||
@ -11,39 +15,33 @@ const globOptions = {
|
|||||||
absolute: true,
|
absolute: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = async function () {
|
log.info('Configurando DB.');
|
||||||
const server = this;
|
|
||||||
|
|
||||||
server.log.info('Configurando DB.');
|
const sequelize = new Sequelize(
|
||||||
|
config.database.database,
|
||||||
|
config.database.username,
|
||||||
|
config.database.password,
|
||||||
|
config.database, {
|
||||||
|
dialect: 'mysql',
|
||||||
|
operatorAliases: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const sequelize = new Sequelize(
|
const db = {};
|
||||||
server.config.database.database,
|
db.sequelize = sequelize;
|
||||||
server.config.database.username,
|
db.Sequelize = Sequelize;
|
||||||
server.config.database.password,
|
|
||||||
server.config.database, {
|
|
||||||
dialect: 'mysql',
|
|
||||||
operatorAliases: false
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const db = {};
|
glob.sync("**/*.model.js", globOptions)
|
||||||
db.sequelize = sequelize;
|
.forEach(function (file) {
|
||||||
db.Sequelize = Sequelize;
|
var model = sequelize.import(file);
|
||||||
|
log.info('Loading "' + model.name + '" model.');
|
||||||
glob.sync("**/*.model.js", globOptions)
|
db[model.name] = model;
|
||||||
.forEach(function (file) {
|
|
||||||
var model = sequelize.import(file);
|
|
||||||
log.info('Loading "' + model.name + '" model.');
|
|
||||||
db[model.name] = model;
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(db).forEach(function (modelName) {
|
|
||||||
if (db[modelName].associate) {
|
|
||||||
db[modelName].associate(db);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
server.log.debug(db);
|
Object.keys(db).forEach(function (modelName) {
|
||||||
|
if (db[modelName].associate) {
|
||||||
|
db[modelName].associate(db);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return db;
|
module.exports = db;
|
||||||
}
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
// Dependencies.
|
|
||||||
const glob = require('glob');
|
|
||||||
const { setWith, merge, get, isObject, isFunction } = require('lodash');
|
|
||||||
|
|
||||||
const optionalPath = path => {
|
|
||||||
return path
|
|
||||||
.replace(/(\.settings|.json|.js)/g, '')
|
|
||||||
.split('/')
|
|
||||||
.slice(1, path.split('/').length - 1)
|
|
||||||
.join('.')
|
|
||||||
.toLowerCase();
|
|
||||||
};
|
|
||||||
|
|
||||||
const aggregatePath = path => {
|
|
||||||
return path
|
|
||||||
.replace(/(\.settings|.json|.js)/g, '')
|
|
||||||
.split('/')
|
|
||||||
.slice(1)
|
|
||||||
.join('.')
|
|
||||||
.toLowerCase();
|
|
||||||
};
|
|
||||||
|
|
||||||
const setConfig = function (ctx, path, type, loader) {
|
|
||||||
const objPath = type === 'optional' ?
|
|
||||||
optionalPath(path) :
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function() {
|
|
||||||
return Promise.all([
|
|
||||||
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
// Load configurations.
|
|
||||||
glob('./modules/*/!(config)/*.*(js|json)', {
|
|
||||||
cwd: this.config.appPath
|
|
||||||
}, (err, files) => {
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
files.map(p => setConfig(this, p, 'aggregate', this.loadFile));
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
@ -12,8 +12,7 @@ const globOptions = {
|
|||||||
absolute: true,
|
absolute: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
module.exports = async function () {
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get('/_health', (req, res, next) => {
|
router.get('/_health', (req, res, next) => {
|
||||||
@ -24,9 +23,8 @@ module.exports = async function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
glob.sync("*/config/routes.js", globOptions)
|
glob.sync("**/*.routes.js", globOptions)
|
||||||
.forEach(function (file) {
|
.forEach(function (file) {
|
||||||
console.log(file);
|
|
||||||
router.use('/v2', require(file));
|
router.use('/v2', require(file));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,29 +1,26 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const config = require('../config');
|
||||||
|
const { logger } = require('../core');
|
||||||
|
|
||||||
|
const JwtStrategy = require('passport-jwt').Strategy;
|
||||||
|
const { ExtractJwt } = require('passport-jwt');
|
||||||
|
//const User = this.models.User;
|
||||||
|
|
||||||
module.exports.jwt = async function() {
|
const jwtOptions = {
|
||||||
const config = this.config;
|
secretOrKey: config.session.secret_token,
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
|
||||||
|
};
|
||||||
|
|
||||||
const JwtStrategy = require('passport-jwt').Strategy;
|
const jwt = async (payload, done) => {
|
||||||
const { ExtractJwt } = require('passport-jwt');
|
logger.info(payload);
|
||||||
//const User = this.models.User;
|
try {
|
||||||
|
//const user = await User.findById(payload.sub);
|
||||||
|
//if (user) return done(null, user);
|
||||||
|
return done(null, false);
|
||||||
|
} catch (error) {
|
||||||
|
return done(error, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const jwtOptions = {
|
module.exports.jwt = new JwtStrategy(jwtOptions, jwt);
|
||||||
secretOrKey: config.session.secret_token,
|
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
|
|
||||||
};
|
|
||||||
|
|
||||||
const jwt = async (payload, done) => {
|
|
||||||
this.log.info(payload);
|
|
||||||
try {
|
|
||||||
//const user = await User.findById(payload.sub);
|
|
||||||
//if (user) return done(null, user);
|
|
||||||
return done(null, false);
|
|
||||||
} catch (error) {
|
|
||||||
return done(error, false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return new JwtStrategy(jwtOptions, jwt);
|
|
||||||
}
|
|
||||||
16
modules/blog/blog.routes.js
Normal file
16
modules/blog/blog.routes.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
const routes = require('express').Router();
|
||||||
|
const models = require('../../core/models');
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
routes.get('/posts', function (req, res) {
|
||||||
|
data = postController.find(req)
|
||||||
|
res.status(200).json(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = routes;
|
||||||
36
modules/blog/category.model.js
Executable file
36
modules/blog/category.model.js
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
module.exports = function (sequelize, DataTypes) {
|
||||||
|
const Category = sequelize.define('Category', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
// Se cambia el nombre del campo de 'order' a 'sort' porque 'order' es una palabra reservada SQL y da problemas al generar las consultas SQL.
|
||||||
|
field: 'order', // <- Chapuza!! El nombre del campo en MySQL es una palabra reservada en SQL.
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
defaultValue: 0,
|
||||||
|
allowNull: false,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'category',
|
||||||
|
freezeTableName: true,
|
||||||
|
timestamps: false
|
||||||
|
});
|
||||||
|
|
||||||
|
Category.beforeCreate((category) => {
|
||||||
|
category.dataValues.id = Math.floor(Math.random() * (999 - 8)) + 8;
|
||||||
|
})
|
||||||
|
|
||||||
|
Category.associate = function (models) {
|
||||||
|
Category.Posts = Category.belongsToMany(models.Post, {
|
||||||
|
through: models.PostCategory,
|
||||||
|
foreignKey: 'categoryId'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return Category;
|
||||||
|
};
|
||||||
20
modules/blog/post-category.model.js
Executable file
20
modules/blog/post-category.model.js
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
module.exports = function (sequelize, DataTypes) {
|
||||||
|
const PostCategory = sequelize.define('PostCategory', {
|
||||||
|
postId: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
primaryKey: true,
|
||||||
|
foreignKey: true
|
||||||
|
},
|
||||||
|
categoryId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
foreignKey: true
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'post-category',
|
||||||
|
freezeTableName: true,
|
||||||
|
timestamps: false
|
||||||
|
});
|
||||||
|
|
||||||
|
return PostCategory;
|
||||||
|
};
|
||||||
76
modules/blog/post.controller.js
Normal file
76
modules/blog/post.controller.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post.js controller
|
||||||
|
*
|
||||||
|
* @description: A set of functions called "actions" for managing `Post`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function (modelService) {
|
||||||
|
return {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve post records.
|
||||||
|
*
|
||||||
|
* @return {Object|Array}
|
||||||
|
*/
|
||||||
|
|
||||||
|
find: async (ctx) => {
|
||||||
|
if (ctx.query._q) {
|
||||||
|
return modelService.search(ctx.query);
|
||||||
|
} else {
|
||||||
|
return modelService.fetchAll(ctx.query);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a post record.
|
||||||
|
*
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
findOne: async (ctx) => {
|
||||||
|
return modelService.fetch(ctx.params);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count post records.
|
||||||
|
*
|
||||||
|
* @return {Number}
|
||||||
|
*/
|
||||||
|
|
||||||
|
count: async (ctx) => {
|
||||||
|
return modelService.count(ctx.query);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a/an post record.
|
||||||
|
*
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
create: async (ctx) => {
|
||||||
|
return modelService.add(ctx.request.body);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a/an post record.
|
||||||
|
*
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
update: async (ctx, next) => {
|
||||||
|
return modelService.edit(ctx.params, ctx.request.body);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a/an post record.
|
||||||
|
*
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
destroy: async (ctx, next) => {
|
||||||
|
return modelService.remove(ctx.params);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
51
modules/blog/post.model.js
Normal file
51
modules/blog/post.model.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function (sequelize, DataTypes) {
|
||||||
|
const Post = sequelize.define('Post', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
defaultValue: DataTypes.UUIDV4,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
/*userId: {
|
||||||
|
type: DataTypes.UUID,
|
||||||
|
allowNull: true,
|
||||||
|
primaryKey: false,
|
||||||
|
unique: false,
|
||||||
|
foreignKey: true
|
||||||
|
},*/
|
||||||
|
date: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: DataTypes.NOW
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: ""
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
tableName: 'post',
|
||||||
|
freezeTableName: true,
|
||||||
|
timestamps: true,
|
||||||
|
updatedAt: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
Post.associate = function (models) {
|
||||||
|
Post.Categories = Post.belongsToMany(models.Category, {
|
||||||
|
through: models.PostCategory,
|
||||||
|
foreignKey: 'postId'
|
||||||
|
});
|
||||||
|
//Post.Comments = Post.hasMany(models.PostComment, { foreignKey: 'postId' });
|
||||||
|
//Post.Reactions = Post.hasMany(models.PostReaction, { foreignKey: 'postId' });
|
||||||
|
//Post.User = Post.belongsTo(models.User, { foreignKey: 'userId' });
|
||||||
|
};
|
||||||
|
return Post;
|
||||||
|
};
|
||||||
252
modules/blog/post.service.js
Normal file
252
modules/blog/post.service.js
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/* global Post */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post.js service
|
||||||
|
*
|
||||||
|
* @description: A set of functions similar to controller's actions to avoid code duplication.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Public dependencies.
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
module.exports = function (Post) {
|
||||||
|
return {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to fetch all posts.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fetchAll: (params) => {
|
||||||
|
// 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);*/
|
||||||
|
|
||||||
|
/*return Post.query(function (qb) {
|
||||||
|
_.forEach(filters.where, (where, key) => {
|
||||||
|
if (_.isArray(where.value) && where.symbol !== 'IN' && where.symbol !== 'NOT IN') {
|
||||||
|
for (const value in where.value) {
|
||||||
|
qb[value ? 'where' : 'orWhere'](key, where.symbol, where.value[value])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qb.where(key, where.symbol, where.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (filters.sort) {
|
||||||
|
qb.orderBy(filters.sort.key, filters.sort.order);
|
||||||
|
}
|
||||||
|
|
||||||
|
qb.offset(filters.start);
|
||||||
|
qb.limit(filters.limit);
|
||||||
|
}).fetchAll({
|
||||||
|
withRelated: filters.populate || populate
|
||||||
|
});*/
|
||||||
|
|
||||||
|
const findOptions = {}
|
||||||
|
|
||||||
|
return Post.findAll(findOptions);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to fetch a/an post.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fetch: (params) => {
|
||||||
|
// Select field to populate.
|
||||||
|
const populate = Post.associations
|
||||||
|
.filter(ast => ast.autoPopulate !== false)
|
||||||
|
.map(ast => ast.alias);
|
||||||
|
|
||||||
|
return Post.forge(_.pick(params, 'id')).fetch({
|
||||||
|
withRelated: populate
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to count a/an post.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
count: (params) => {
|
||||||
|
// Convert `params` object to filters compatible with Bookshelf.
|
||||||
|
const filters = strapi.utils.models.convertParams('post', params);
|
||||||
|
|
||||||
|
return Post.query(function (qb) {
|
||||||
|
_.forEach(filters.where, (where, key) => {
|
||||||
|
if (_.isArray(where.value)) {
|
||||||
|
for (const value in where.value) {
|
||||||
|
qb[value ? 'where' : 'orWhere'](key, where.symbol, where.value[value]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qb.where(key, where.symbol, where.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).count();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to add a/an post.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
add: async (values) => {
|
||||||
|
// 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(data).save();
|
||||||
|
|
||||||
|
// Create relational data and return the entry.
|
||||||
|
return Post.updateRelations({
|
||||||
|
id: entry.id,
|
||||||
|
values: relations
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to edit a/an post.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
edit: async (params, values) => {
|
||||||
|
// 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
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to remove a/an post.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
remove: async (params) => {
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise to search a/an post.
|
||||||
|
*
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
|
||||||
|
search: async (params) => {
|
||||||
|
// 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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,12 +0,0 @@
|
|||||||
const routes = require('express').Router();
|
|
||||||
|
|
||||||
routes.use((req, res, next) => {
|
|
||||||
// here we can access the req.params object and make auth checks
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
routes.get('/posts', function (req, res) {
|
|
||||||
res.status(200).json({ message: 'Connected!' });
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = routes;
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Post.js controller
|
|
||||||
*
|
|
||||||
* @description: A set of functions called "actions" for managing `Post`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve post records.
|
|
||||||
*
|
|
||||||
* @return {Object|Array}
|
|
||||||
*/
|
|
||||||
|
|
||||||
find: async (ctx) => {
|
|
||||||
if (ctx.query._q) {
|
|
||||||
return strapi.services.post.search(ctx.query);
|
|
||||||
} else {
|
|
||||||
return strapi.services.post.fetchAll(ctx.query);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a post record.
|
|
||||||
*
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
findOne: async (ctx) => {
|
|
||||||
return strapi.services.post.fetch(ctx.params);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Count post records.
|
|
||||||
*
|
|
||||||
* @return {Number}
|
|
||||||
*/
|
|
||||||
|
|
||||||
count: async (ctx) => {
|
|
||||||
return strapi.services.post.count(ctx.query);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a/an post record.
|
|
||||||
*
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
create: async (ctx) => {
|
|
||||||
return strapi.services.post.add(ctx.request.body);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a/an post record.
|
|
||||||
*
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
update: async (ctx, next) => {
|
|
||||||
return strapi.services.post.edit(ctx.params, ctx.request.body) ;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy a/an post record.
|
|
||||||
*
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
|
|
||||||
destroy: async (ctx, next) => {
|
|
||||||
return strapi.services.post.remove(ctx.params);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lifecycle callbacks for the `Post` model.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
// Before saving a value.
|
|
||||||
// Fired before an `insert` or `update` query.
|
|
||||||
// beforeSave: async (model, attrs, options) => {},
|
|
||||||
|
|
||||||
// After saving a value.
|
|
||||||
// Fired after an `insert` or `update` query.
|
|
||||||
// afterSave: async (model, response, options) => {},
|
|
||||||
|
|
||||||
// Before fetching a value.
|
|
||||||
// Fired before a `fetch` operation.
|
|
||||||
// beforeFetch: async (model, columns, options) => {},
|
|
||||||
|
|
||||||
// After fetching a value.
|
|
||||||
// Fired after a `fetch` operation.
|
|
||||||
// afterFetch: async (model, response, options) => {},
|
|
||||||
|
|
||||||
// Before fetching all values.
|
|
||||||
// Fired before a `fetchAll` operation.
|
|
||||||
// beforeFetchAll: async (model, columns, options) => {},
|
|
||||||
|
|
||||||
// After fetching all values.
|
|
||||||
// Fired after a `fetchAll` operation.
|
|
||||||
// afterFetchAll: async (model, response, options) => {},
|
|
||||||
|
|
||||||
// Before creating a value.
|
|
||||||
// Fired before an `insert` query.
|
|
||||||
// beforeCreate: async (model, attrs, options) => {},
|
|
||||||
|
|
||||||
// After creating a value.
|
|
||||||
// Fired after an `insert` query.
|
|
||||||
// afterCreate: async (model, attrs, options) => {},
|
|
||||||
|
|
||||||
// Before updating a value.
|
|
||||||
// Fired before an `update` query.
|
|
||||||
// beforeUpdate: async (model, attrs, options) => {},
|
|
||||||
|
|
||||||
// After updating a value.
|
|
||||||
// Fired after an `update` query.
|
|
||||||
// afterUpdate: async (model, attrs, options) => {},
|
|
||||||
|
|
||||||
// Before destroying a value.
|
|
||||||
// Fired before a `delete` query.
|
|
||||||
// beforeDestroy: async (model, attrs, options) => {},
|
|
||||||
|
|
||||||
// After destroying a value.
|
|
||||||
// Fired after a `delete` query.
|
|
||||||
// afterDestroy: async (model, attrs, options) => {}
|
|
||||||
};
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"connection": "default",
|
|
||||||
"collectionName": "posts",
|
|
||||||
"info": {
|
|
||||||
"name": "post",
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"increments": true,
|
|
||||||
"timestamps": true,
|
|
||||||
"comment": ""
|
|
||||||
},
|
|
||||||
"attributes": {
|
|
||||||
"Título": {
|
|
||||||
"default": "",
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,243 +0,0 @@
|
|||||||
/* global Post */
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Post.js service
|
|
||||||
*
|
|
||||||
* @description: A set of functions similar to controller's actions to avoid code duplication.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Public dependencies.
|
|
||||||
const _ = require('lodash');
|
|
||||||
|
|
||||||
// Strapi utilities.
|
|
||||||
//const utils = require('strapi-hook-bookshelf/lib/utils/');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Promise to fetch all posts.
|
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fetchAll: (params) => {
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
return Post.query(function(qb) {
|
|
||||||
_.forEach(filters.where, (where, key) => {
|
|
||||||
if (_.isArray(where.value) && where.symbol !== 'IN' && where.symbol !== 'NOT IN') {
|
|
||||||
for (const value in where.value) {
|
|
||||||
qb[value ? 'where' : 'orWhere'](key, where.symbol, where.value[value])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qb.where(key, where.symbol, where.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (filters.sort) {
|
|
||||||
qb.orderBy(filters.sort.key, filters.sort.order);
|
|
||||||
}
|
|
||||||
|
|
||||||
qb.offset(filters.start);
|
|
||||||
qb.limit(filters.limit);
|
|
||||||
}).fetchAll({
|
|
||||||
withRelated: filters.populate || populate
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Promise to fetch a/an post.
|
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
fetch: (params) => {
|
|
||||||
// Select field to populate.
|
|
||||||
const populate = Post.associations
|
|
||||||
.filter(ast => ast.autoPopulate !== false)
|
|
||||||
.map(ast => ast.alias);
|
|
||||||
|
|
||||||
return Post.forge(_.pick(params, 'id')).fetch({
|
|
||||||
withRelated: populate
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Promise to count a/an post.
|
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
count: (params) => {
|
|
||||||
// Convert `params` object to filters compatible with Bookshelf.
|
|
||||||
const filters = strapi.utils.models.convertParams('post', params);
|
|
||||||
|
|
||||||
return Post.query(function(qb) {
|
|
||||||
_.forEach(filters.where, (where, key) => {
|
|
||||||
if (_.isArray(where.value)) {
|
|
||||||
for (const value in where.value) {
|
|
||||||
qb[value ? 'where' : 'orWhere'](key, where.symbol, where.value[value]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qb.where(key, where.symbol, where.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).count();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Promise to add a/an post.
|
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
add: async (values) => {
|
|
||||||
// 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(data).save();
|
|
||||||
|
|
||||||
// Create relational data and return the entry.
|
|
||||||
return Post.updateRelations({ id: entry.id , values: relations });
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Promise to edit a/an post.
|
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
edit: async (params, values) => {
|
|
||||||
// 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 }));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Promise to remove a/an post.
|
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
remove: async (params) => {
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Promise to search a/an post.
|
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
*/
|
|
||||||
|
|
||||||
search: async (params) => {
|
|
||||||
// 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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -20,6 +20,7 @@
|
|||||||
"nodemon": "^1.18.9"
|
"nodemon": "^1.18.9"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"args-list": "^0.3.3",
|
||||||
"async": "^2.6.2",
|
"async": "^2.6.2",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.3",
|
||||||
"buffer": "^5.2.1",
|
"buffer": "^5.2.1",
|
||||||
@ -27,7 +28,6 @@
|
|||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"events": "^3.0.0",
|
|
||||||
"express": "^4.16.4",
|
"express": "^4.16.4",
|
||||||
"express-validation": "^1.0.2",
|
"express-validation": "^1.0.2",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
|
|||||||
188
server.js
188
server.js
@ -1,136 +1,76 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { EventEmitter } = require('events');
|
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
const { toLower } = require('lodash');
|
const { assign, toLower } = require('lodash');
|
||||||
const utils = require('./utils');
|
|
||||||
|
|
||||||
const {
|
const config = require('./config');
|
||||||
express,
|
const { express, logger, models } = require('./core');
|
||||||
logger,
|
|
||||||
nestedConfigurations,
|
|
||||||
appConfigurations
|
|
||||||
} = require('./core');
|
|
||||||
|
|
||||||
class Server extends EventEmitter {
|
const currentState = assign({
|
||||||
constructor() {
|
launchedAt: Date.now(),
|
||||||
super();
|
appPath: process.cwd(),
|
||||||
|
host: process.env.HOST || process.env.HOSTNAME || 'localhost',
|
||||||
|
port: process.env.PORT || 1337,
|
||||||
|
environment: toLower(process.env.NODE_ENV) || 'development',
|
||||||
|
connections: {}
|
||||||
|
}, config);
|
||||||
|
|
||||||
this.setMaxListeners(100);
|
function stop(server) {
|
||||||
|
// Destroy server and available connections.
|
||||||
// Logger.
|
if (server) {
|
||||||
this.log = logger;
|
server.close();
|
||||||
|
|
||||||
// Default configurations.
|
|
||||||
this.config = {
|
|
||||||
launchedAt: Date.now(),
|
|
||||||
appPath: process.cwd(),
|
|
||||||
host: process.env.HOST || process.env.HOSTNAME || 'localhost',
|
|
||||||
port: process.env.PORT || 1337,
|
|
||||||
environment: toLower(process.env.NODE_ENV) || 'development',
|
|
||||||
environments: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Bind context functions.
|
|
||||||
this.loadFile = utils.loadFile.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
process.send('stop');
|
||||||
this.emit('server:init');
|
// Kill process.
|
||||||
await nestedConfigurations.call(this);
|
process.exit(1);
|
||||||
await appConfigurations.call(this);
|
|
||||||
|
|
||||||
/*await Promise.all([
|
|
||||||
modules.call(this),
|
|
||||||
]).then(results => {
|
|
||||||
this.modules = results[0];
|
|
||||||
});*/
|
|
||||||
}
|
|
||||||
|
|
||||||
async start() {
|
|
||||||
try {
|
|
||||||
|
|
||||||
// Emit starting event.
|
|
||||||
this.emit('server:starting');
|
|
||||||
|
|
||||||
await this.init();
|
|
||||||
|
|
||||||
// Expose `express`.
|
|
||||||
this.app = await express.call(this);
|
|
||||||
|
|
||||||
// Mount the HTTP server.
|
|
||||||
this.server = http.createServer(this.app);
|
|
||||||
await this.enhancer.call(this);
|
|
||||||
|
|
||||||
// Launch server.
|
|
||||||
this.server.listen(this.config.port, async (err) => {
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
this.log.debug(`⚠️ Server wasn't able to start properly.`);
|
|
||||||
this.log.error(err);
|
|
||||||
return this.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log.info('Time: ' + new Date());
|
|
||||||
this.log.info('Launched in: ' + (Date.now() - this.config.launchedAt) + ' ms');
|
|
||||||
this.log.info('Environment: ' + this.config.environment);
|
|
||||||
this.log.info('Process PID: ' + process.pid);
|
|
||||||
//this.log.info(`Version: ${this.config.info.strapi} (node v${this.config.info.node})`);
|
|
||||||
this.log.info('To shut down your server, press <CTRL> + C at any time');
|
|
||||||
this.log.info(`⚡️ Server: ${this.config.url}`);
|
|
||||||
|
|
||||||
// Emit started event.
|
|
||||||
this.emit('server:started');
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
this.log.debug(`⛔️ Server wasn't able to start properly.`);
|
|
||||||
this.log.error(err);
|
|
||||||
console.log(err);
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async enhancer() {
|
|
||||||
this.connections = {};
|
|
||||||
|
|
||||||
this.server.on('connection', conn => {
|
|
||||||
const key = conn.remoteAddress + ':' + conn.remotePort;
|
|
||||||
this.connections[key] = conn;
|
|
||||||
|
|
||||||
conn.on('close', () => {
|
|
||||||
delete this.connections[key];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.server.on('error', err => {
|
|
||||||
if (err.code === 'EADDRINUSE') {
|
|
||||||
this.log.debug(`⛔️ Server wasn't able to start properly.`);
|
|
||||||
this.log.error(`The port ${err.port} is already used by another application.`);
|
|
||||||
this.stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
this.emit('server:stoping');
|
|
||||||
|
|
||||||
// Destroy server and available connections.
|
|
||||||
if (this.server) {
|
|
||||||
this.server.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
process.send('stop');
|
|
||||||
// Kill process.
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const server = http.createServer(express);
|
||||||
|
server.on('connection', conn => {
|
||||||
|
const key = conn.remoteAddress + ':' + conn.remotePort;
|
||||||
|
currentState.connections[key] = conn;
|
||||||
|
|
||||||
(() => {
|
conn.on('close', () => {
|
||||||
const server = new Server();
|
delete currentState.connections[key];
|
||||||
server.start();
|
});
|
||||||
})();
|
});
|
||||||
|
|
||||||
|
server.on('error', err => {
|
||||||
|
if (err.code === 'EADDRINUSE') {
|
||||||
|
logger.debug(`⛔️ Server wasn't able to start properly.`);
|
||||||
|
logger.error(`The port ${err.port} is already used by another application.`);
|
||||||
|
stop(server);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
models.sequelize.sync().then(() => {
|
||||||
|
// Launch server.
|
||||||
|
server.listen(currentState.port, (err) => {
|
||||||
|
if (err) {
|
||||||
|
logger.debug(`⚠️ Server wasn't able to start properly.`);
|
||||||
|
logger.error(err);
|
||||||
|
return stop(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('Time: ' + new Date());
|
||||||
|
logger.info('Launched in: ' + (Date.now() - currentState.launchedAt) + ' ms');
|
||||||
|
logger.info('Environment: ' + currentState.environment);
|
||||||
|
logger.info('Process PID: ' + process.pid);
|
||||||
|
//logger.info(`Version: ${this.config.info.strapi} (node v${this.config.info.node})`);
|
||||||
|
logger.info('To shut down your server, press <CTRL> + C at any time');
|
||||||
|
logger.info(`⚡️ Server: http://${currentState.host}:${currentState.port}`);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.debug(`⛔️ Server wasn't able to start properly.`);
|
||||||
|
logger.error(err);
|
||||||
|
console.error(err);
|
||||||
|
stop(server);
|
||||||
|
}
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@ -119,6 +119,11 @@ argparse@^1.0.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
sprintf-js "~1.0.2"
|
sprintf-js "~1.0.2"
|
||||||
|
|
||||||
|
args-list@^0.3.3:
|
||||||
|
version "0.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/args-list/-/args-list-0.3.3.tgz#d3fa5b7fe49ec96efb1e99adf20e7cfacc179956"
|
||||||
|
integrity sha1-0/pbf+SeyW77Hpmt8g58+swXmVY=
|
||||||
|
|
||||||
arr-diff@^4.0.0:
|
arr-diff@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
|
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
|
||||||
@ -971,11 +976,6 @@ etag@~1.8.1:
|
|||||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||||
|
|
||||||
events@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
|
|
||||||
integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==
|
|
||||||
|
|
||||||
execa@^0.7.0:
|
execa@^0.7.0:
|
||||||
version "0.7.0"
|
version "0.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
|
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user