diff --git a/config/environments/development.js b/config/environments/development.js index b468fd0..6b94542 100644 --- a/config/environments/development.js +++ b/config/environments/development.js @@ -21,6 +21,12 @@ module.exports = { hostname: "https://cdnapp2.loquedeverdadimporta.org", }, + uploads: { + path: "/opt/app2/cdn2", + thumb_size: 300, + thumb_prefix: "th_", + }, + paginate: { limit: 10, maxLimit: 50 diff --git a/config/environments/production.js b/config/environments/production.js index 0d820f0..695ebac 100644 --- a/config/environments/production.js +++ b/config/environments/production.js @@ -6,37 +6,43 @@ module.exports = { host: 'localhost', dialect: 'mysql', -/* + /* username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, host: process.env.DB_HOSTNAME, dialect: 'mysql', -*/ + */ }, - session: { - secret_token: process.env.SECRET_TOKEN || "B57J=7B`NQ$y98|~5;hc715bo09^5oz8NR+]n9r~215B91Nd9P%25_N6r!GHcOKp|18y5-73Dr5^@9k7n]5l<-41D1o", - token_expires_in: '365d' - }, + session: { + secret_token: process.env.SECRET_TOKEN || "B57J=7B`NQ$y98|~5;hc715bo09^5oz8NR+]n9r~215B91Nd9P%25_N6r!GHcOKp|18y5-73Dr5^@9k7n]5l<-41D1o", + token_expires_in: '365d' + }, - server: { - hostname: process.env.HOSTNAME || '127.0.0.1', - port: process.env.PORT || 18888 - }, - - cdn: { - hostname: "https://cdnapp2.loquedeverdadimporta.org", - }, + server: { + hostname: process.env.HOSTNAME || '127.0.0.1', + port: process.env.PORT || 18888 + }, - paginate: { - limit: 10, - maxLimit: 50 - }, + cdn: { + hostname: "https://cdnapp2.loquedeverdadimporta.org", + }, - vimeo: { - CLIENT_ID: '9581f2d0ed211dc1e31b5c825117dc1f00d77ba0', - CLIENT_SECRET: '3kf0hQ63OXSPm7z2X1qyOV4iH57Xha0cyRXn0VJneq8zHbWKjfB1/9H8KcgBgzgkzw0Y9x/xFLUAauuHLEkqHxLBw8QueanCgIZev1L5xoksrKuKX7gMvErqqP+uFNnA', - ACCESS_TOKEN: 'fcc7b31fe690a768efa920e13d750449' - } + uploads: { + path: "/opt/app2/cdn2", + thumb_size: 300, + thumb_prefix: "th_", + }, + + paginate: { + limit: 10, + maxLimit: 50 + }, + + vimeo: { + CLIENT_ID: '9581f2d0ed211dc1e31b5c825117dc1f00d77ba0', + CLIENT_SECRET: '3kf0hQ63OXSPm7z2X1qyOV4iH57Xha0cyRXn0VJneq8zHbWKjfB1/9H8KcgBgzgkzw0Y9x/xFLUAauuHLEkqHxLBw8QueanCgIZev1L5xoksrKuKX7gMvErqqP+uFNnA', + ACCESS_TOKEN: 'fcc7b31fe690a768efa920e13d750449' + } } diff --git a/core/controllers/index.js b/core/controllers/index.js index 8745e5e..d9f35a5 100644 --- a/core/controllers/index.js +++ b/core/controllers/index.js @@ -2,7 +2,7 @@ const _find = require('./find'); const httpStatus = require('http-status'); const { extractParamsFromRequest, handleErrorResponse, handleResultResponse } = require('../../helpers/controller.helper'); -function buildContext(req, config) { +function buildContext(req, config ) { return { user: req.user, scopes: [], diff --git a/core/passport.js b/core/passport.js index 832e668..9fb219e 100644 --- a/core/passport.js +++ b/core/passport.js @@ -100,7 +100,6 @@ passport.use('local-phone', new LocalStrategy(localPhoneOptions, async (phone, f passport.use('jwt', new CustomStrategy(async (req, done) => { const token = ((req && req.headers && req.headers['x-access-token']) ? req.headers['x-access-token'] : null); - console.log('PASSPORT JWT', token); if (!token) { return done(null, false, { message: 'Unauthorized. Token missing.'}); @@ -110,10 +109,8 @@ passport.use('jwt', new CustomStrategy(async (req, done) => { if (result) { //recuperamos el usuario de la petición - console.log(result); let user = await authService.extraMethods.findUser({ id: result.id }); if (user) { - console.log('Usuario válido'); user = user.toJSON(); delete user.password; return done(null, user, { message: 'Logged in Successfully' }); diff --git a/helpers/cdn.helper.js b/helpers/cdn.helper.js index 2e24b84..60dbbfc 100644 --- a/helpers/cdn.helper.js +++ b/helpers/cdn.helper.js @@ -1,15 +1,20 @@ +const Path = require("path"); const config = require('../config'); +const sanitize = require("sanitize-filename"); +const safename = require("safename"); const assetsUrl = config.cdn.hostname; const CDN_PATHS = { BLOG: "blog/", CITIES: "cities/", + EVENT: "events/", PROFILE: "profile/", SPEAKERS: "speakers/", WALLPAPERS: "wallpapers/" }; +const sanitizeFilename = (filename) => safename(sanitize(filename, { replacement: "_" })); const getCDNMediaUrl = (mediaUri) => { if (mediaUri) { @@ -23,9 +28,29 @@ const getCDNMediaUrl = (mediaUri) => { const getCDNCityMediaUrl = (cityName) => encodeURI(`${assetsUrl}/${CDN_PATHS.CITIES}/${cityName}.jpg`); +const getCDNFilenameWithPath = (filename, type) => { + var cdnPath = ''; + switch (type) { + case 'speaker': + cdnPath = CDN_PATHS.SPEAKERS; + break; + case 'event': + cdnPath = CDN_PATHS.EVENT; + break; + case 'post': + cdnPath = CDN_PATHS.BLOG; + break; + + default: + throw new Error('Multimedia type unknown'); + } + return Path.join(cdnPath, sanitizeFilename(filename)); +} module.exports = { + CDN_PATHS, getCDNCityMediaUrl, - getCDNMediaUrl + getCDNMediaUrl, + getCDNFilenameWithPath } \ No newline at end of file diff --git a/modules/multimedia/multimedia.controller.js b/modules/multimedia/multimedia.controller.js index 2354049..197295f 100644 --- a/modules/multimedia/multimedia.controller.js +++ b/modules/multimedia/multimedia.controller.js @@ -1,14 +1,41 @@ 'use strict'; - +const httpStatus = require('http-status'); const generateControllers = require('../../core/controllers'); +const { extractParamsFromRequest, handleErrorResponse, handleResultResponse } = require('../../helpers/controller.helper'); const multimediaService = require('./multimedia.service'); - +const multimediaFileService = require('./multimedia_file.service'); +const cdnHelper = require('../../helpers/cdn.helper'); // Module Name const MODULE_NAME = '[multimedia.controller]'; const controllerOptions = { MODULE_NAME }; -const extraControllers = {}; +const extraControllers = { + + create: (config) => { + return async (req, res, next) => { + try { + var file = req.file; + var data = req.body; + console.log(file); + console.log(data); + + // ¿Hay fichero? + if (!file || !file.buffer) { + return handleResultResponse('Multimediafile is missing', null, null, res, httpStatus.BAD_REQUEST); + } + + var CDNFilePath = cdnHelper.getCDNFilenameWithPath(file.originalname, 'speaker'); + await multimediaFileService.uploadFile(file, CDNFilePath); + + const result = await multimediaService.create(data, buildContext(req, config)); + return handleResultResponse(result, null, null, res, httpStatus.CREATED) + } catch (error) { + return handleErrorResponse(MODULE_NAME, 'create', error, res) + } + } + }, +}; module.exports = generateControllers(multimediaService, extraControllers, controllerOptions); diff --git a/modules/multimedia/multimedia.routes.js b/modules/multimedia/multimedia.routes.js index 9e56df6..00a308a 100644 --- a/modules/multimedia/multimedia.routes.js +++ b/modules/multimedia/multimedia.routes.js @@ -1,4 +1,5 @@ const routes = require('express').Router(); +const Joi = require('joi'); const { isAdministratorUser, isLoggedUser } = require('../../middlewares/accessValidator'); const { cacheSuccesses } = require('../../middlewares/cache'); @@ -6,7 +7,7 @@ const { cacheSuccesses } = require('../../middlewares/cache'); // Uploader const uploader = require('../../middlewares/uploader.js'); -//const SchemaValidator = require('../../middlewares/schemaValidator'); +const SchemaValidator = require('../../middlewares/schemaValidator'); const PaginateMiddleware = require('../../middlewares/paginate'); const FieldMiddleware = require('../../middlewares/fields'); @@ -49,47 +50,28 @@ routes.get('/multimedias/:id', //cacheSuccesses('24 hours'), ********************************************************************************************************* */ +const multimediasInputType = Joi.object().keys({ + entityId: Joi.string().required(), + entityName: Joi.string().required(), + name: Joi.string().optional(), + description: Joi.string().optional(), + type: Joi.string().required(), + class: Joi.string().required(), +}); + + routes.post('/admin/multimedias/', - isAdministratorUser, uploader.any(), - function (req, res, next) { - - var file = req.file; - console.log(req.file); - console.log(req.files); - /*var filePath = assetHelper.getFileNameWithPath(file.originalname, assetHelper.PATHS.WALLPAPERS); - - assetHelper.uploadImage(file, filePath).then( - function () { - res.status(500).json({ - messenger: { - success: true, - message: 'Speaker wallpaper uploaded successfully', - code: 'E0100X' - }, - data: { - filepath: filePath - } - }); - }, - function (error) { - logger.error(error); - res.status(500).json({ - messenger: { - success: false, - message: 'File updating error.', - code: 'E01006' - } - }); - } - );*/ - res.send('0'); - }, - multimediaController.create + isAdministratorUser, + //SchemaValidator(multimediasInputType, true), + uploader.single('file'), + multimediaController.create() ); routes.put('/admin/multimedias/:id', isAdministratorUser, - multimediaController.update + //SchemaValidator(multimediasInputType, true), + uploader.single('file'), + multimediaController.update() ); diff --git a/modules/multimedia/multimedia_file.service.js b/modules/multimedia/multimedia_file.service.js index 008b044..4fab74a 100644 --- a/modules/multimedia/multimedia_file.service.js +++ b/modules/multimedia/multimedia_file.service.js @@ -2,6 +2,15 @@ 'use strict'; const _ = require('lodash'); +const fs = require('fs'); +const Path = require("path"); +const sanitize = require("sanitize-filename"); +const safename = require("safename"); +const writeFile = require('write'); +const imageType = require('image-type'); +const sharp = require('sharp'); + +const config = require('../../config'); const { generateService, parseParamsToFindOptions } = require('../../helpers/service.helper'); const models = require('../../core/models'); const getVimeoVideoInfo = require('../../helpers/vimeo.helper'); @@ -10,7 +19,76 @@ const providerComposer = (multimedia) => { return getVimeoVideoInfo(multimedia.code) } +const _generateThumbFileName = function (fileName, prefix) { + prefix = prefix || config.uploads.thumb_prefix; + return Path.join( + Path.dirname(fileName), + prefix + Path.basename(fileName) + ); +}; + + const extraMethods = { + removeFile: function (filename) { + + const diskFilename = Path.join(config.uploads.path, filename); + const diskThumbFilename = _generateThumbFileName(diskFilename); + + return new Promise(function(resolve, reject) { + fs.unlink(diskFilename, function (error) { + fs.unlink(diskThumbFilename, function (error) { + resolve(); + }); + }); + }); + }, + + + uploadFile: function (file, filename, options) { + var defaultOptions = { + thumb: false, + thumbSize: config.uploads.thumb_size, + } + + options = _.defaults(options || {}, defaultOptions); + var diskFilename = Path.join(config.uploads.path, filename); + + return new Promise(function(resolve, reject) { + if (!imageType(file.buffer)) { + reject('No es un fichero de imagen'); + }; + + fs.writeFile(diskFilename, file.buffer, function(error) { + if (error) { + console.log(error); + reject(error); + } else { + if (options.thumb) { + var diskThumbFilename = _generateThumbFileName(diskfilename); + sharp(file.buffer).resize(options.thumbSize).toBuffer() + .then(function (data) { + fs.writeFile(diskThumbFilename, data, function(error) { + if (error) { + console.log(error); + reject(error); + } else { + resolve(); + } + }); + }) + .catch(function (error) { + console.log(error); + reject(error); + }); + } else { + resolve(); + } + } + }); + }); + }, + + afterFetchOne: async (result, params, context) => { if (!result) { return result; diff --git a/package.json b/package.json index 6a8f0da..69cc743 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "helmet": "^3.16.0", "http": "^0.0.0", "http-status": "^1.3.2", + "image-type": "^4.1.0", "joi": "^14.3.1", "lodash": "^4.17.11", "mailchimp-api-v3": "^1.12.0", @@ -57,7 +58,10 @@ "rand-token": "^0.4.0", "redis": "^2.8.0", "response-time": "^2.3.2", + "safename": "^1.0.2", + "sanitize-filename": "^1.6.2", "sequelize": "^5.6.1", + "sharp": "^0.23.0", "vimeo": "^2.1.1", "vm": "^0.1.0", "winston": "^3.2.1"