Add upload image dimension check/limitation for rank, squad and decoration images (CC-69)
parent
68ad467e4d
commit
141a340aad
|
@ -4,7 +4,7 @@
|
|||
const codes = require('../routes/http-codes');
|
||||
|
||||
// library to check image dimensions from file buffer
|
||||
var sizeOf = require('buffer-image-size');
|
||||
const sizeOf = require('buffer-image-size');
|
||||
|
||||
/**
|
||||
* check if id has valid UUID format
|
||||
|
@ -27,8 +27,6 @@ const idValidator = (req, res, next) => {
|
|||
|
||||
const imageDimensionValidator = (imageFileBuf, maxWidth, maxHeight) => {
|
||||
const dimensions = sizeOf(imageFileBuf);
|
||||
console.log(dimensions.width)
|
||||
console.log(dimensions.height)
|
||||
if (dimensions.width > maxWidth || dimensions.height > maxHeight) {
|
||||
let err = new Error(`Image exceeds maximum dimensions of ${maxWidth}px width and ${maxHeight}px height`);
|
||||
err.status = codes.wrongrequest;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 53 KiB |
|
@ -13,6 +13,7 @@ const codes = require('./http-codes');
|
|||
const apiAuthenticationMiddleware = require('../middleware/auth-middleware');
|
||||
const checkHl = require('../middleware/permission-check').checkHl;
|
||||
|
||||
const genericGetById = require('./_generic').genericGetById;
|
||||
const routerHandling = require('../middleware/router-handling');
|
||||
const idValidator = require('../middleware/validators').idValidator;
|
||||
const resourceLocation = require('../middleware/resource-location').resourceLocation().concat('/decoration/');
|
||||
|
@ -22,12 +23,16 @@ const DecorationModel = require('../models/decoration');
|
|||
const AwardingsModel = require('../models/awarding');
|
||||
|
||||
// util
|
||||
const genericGetById = require('./_generic').genericGetById;
|
||||
const imageDimensionValidator = require('../middleware/validators').imageDimensionValidator;
|
||||
const MAX_MEDAL_IMAGE_WIDTH = 100;
|
||||
const MAX_MEDAL_IMAGE_HEIGHT = 150;
|
||||
const MAX_RIBBON_IMAGE_WIDTH = 150;
|
||||
const MAX_RIBBON_IMAGE_HEIGHT = 50;
|
||||
|
||||
const decoration = new express.Router();
|
||||
const decorationRouter = new express.Router();
|
||||
|
||||
// routes **********************
|
||||
decoration.route('/')
|
||||
decorationRouter.route('/')
|
||||
.get((req, res, next) => {
|
||||
const filter = {};
|
||||
if (req.query.fractFilter) {
|
||||
|
@ -59,7 +64,17 @@ decoration.route('/')
|
|||
})
|
||||
|
||||
.post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => {
|
||||
if (req.file) {
|
||||
const decoration = new DecorationModel(req.body);
|
||||
|
||||
const imageFileBuffer = req.file.buffer;
|
||||
const err = imageDimensionValidator(imageFileBuffer,
|
||||
(decoration.isMedal ? MAX_MEDAL_IMAGE_WIDTH : MAX_RIBBON_IMAGE_WIDTH),
|
||||
(decoration.isMedal ? MAX_MEDAL_IMAGE_HEIGHT : MAX_RIBBON_IMAGE_HEIGHT));
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// timestamp and default are set automatically by Mongoose Schema Validation
|
||||
decoration.save((err) => {
|
||||
if (err) {
|
||||
|
@ -76,21 +91,28 @@ decoration.route('/')
|
|||
});
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
const err = new Error('no image file provided');
|
||||
err.status = codes.wrongmediasend;
|
||||
next(err);
|
||||
}
|
||||
})
|
||||
|
||||
.all(
|
||||
routerHandling.httpMethodNotAllowed
|
||||
);
|
||||
|
||||
decoration.route('/:id')
|
||||
decorationRouter.route('/:id')
|
||||
.get(idValidator, (req, res, next) => {
|
||||
return genericGetById(req, res, next, DecorationModel);
|
||||
})
|
||||
|
||||
.patch(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => {
|
||||
if (!req.body || (req.body._id && req.body._id !== req.params.id)) {
|
||||
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must match
|
||||
const err = new Error('id of PATCH resource and send JSON body are not equal ' + req.params.id + ' ' +
|
||||
// little bit different as in PUT. :id does not need to be in data, but if the _id and url id must
|
||||
// match
|
||||
const err = new Error(
|
||||
'id of PATCH resource and send JSON body are not equal ' + req.params.id + ' ' +
|
||||
req.body._id);
|
||||
err.status = codes.notfound;
|
||||
next(err);
|
||||
|
@ -101,7 +123,19 @@ decoration.route('/:id')
|
|||
req.body.updatedAt = new Date();
|
||||
req.body.$inc = {__v: 1};
|
||||
|
||||
DecorationModel.findById(req.body._id, (err, item) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (req.file) {
|
||||
const imageFileBuffer = req.file.buffer;
|
||||
const imageDimensionError = imageDimensionValidator(imageFileBuffer,
|
||||
(item.isMedal ? MAX_MEDAL_IMAGE_WIDTH : MAX_RIBBON_IMAGE_WIDTH),
|
||||
(item.isMedal ? MAX_MEDAL_IMAGE_HEIGHT : MAX_RIBBON_IMAGE_HEIGHT));
|
||||
if (imageDimensionError) {
|
||||
return next(imageDimensionError);
|
||||
}
|
||||
|
||||
const file = resourceLocation + req.params.id + '.png';
|
||||
fs.unlink(file, (err) => {
|
||||
if (err) next(err);
|
||||
|
@ -111,8 +145,8 @@ decoration.route('/:id')
|
|||
});
|
||||
}
|
||||
|
||||
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no need
|
||||
// to reset attributes that are missing.
|
||||
// PATCH is easier with mongoose than PUT. You simply update by all data that comes from outside. no
|
||||
// need to reset attributes that are missing.
|
||||
DecorationModel.findByIdAndUpdate(req.params.id, req.body, {new: true}, (err, item) => {
|
||||
if (err) {
|
||||
err.status = codes.wrongrequest;
|
||||
|
@ -124,6 +158,7 @@ decoration.route('/:id')
|
|||
}
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
.delete(apiAuthenticationMiddleware, checkHl, (req, res, next) => {
|
||||
|
@ -142,7 +177,8 @@ decoration.route('/:id')
|
|||
// delete graphic
|
||||
fs.unlink(resourceLocation.concat(id).concat('.png'),
|
||||
(err) => {
|
||||
// we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last handler
|
||||
// we don't set res.locals.items and thus it will send a 204 (no content) at the end. see last
|
||||
// handler
|
||||
res.locals.processed = true;
|
||||
next(err);
|
||||
});
|
||||
|
@ -155,7 +191,7 @@ decoration.route('/:id')
|
|||
|
||||
// this middleware function can be used, if you like or remove it
|
||||
// it looks for object(s) in res.locals.items and if they exist, they are send to the client as json
|
||||
decoration.use(routerHandling.emptyResponse);
|
||||
decorationRouter.use(routerHandling.emptyResponse);
|
||||
|
||||
|
||||
module.exports = decoration;
|
||||
module.exports = decorationRouter;
|
||||
|
|
|
@ -56,14 +56,14 @@ ranks.route('/')
|
|||
})
|
||||
|
||||
.post(apiAuthenticationMiddleware, checkHl, upload.single('image'), (req, res, next) => {
|
||||
const rank = new RankModel(req.body);
|
||||
if (req.file) {
|
||||
const imageFileBuffer = req.file.buffer;
|
||||
|
||||
const err = imageDimensionValidator(imageFileBuffer, MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT);
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
const rank = new RankModel(req.body);
|
||||
// timestamp and default are set automatically by Mongoose Schema Validation
|
||||
rank.save((err) => {
|
||||
if (err) {
|
||||
|
@ -77,6 +77,11 @@ ranks.route('/')
|
|||
next(err);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const err = new Error('no image file provided');
|
||||
err.status = codes.wrongmediasend;
|
||||
next(err);
|
||||
}
|
||||
})
|
||||
|
||||
.all(
|
||||
|
|
|
@ -22,6 +22,9 @@ const SquadModel = require('../models/squad');
|
|||
|
||||
// util
|
||||
const genericGetById = require('./_generic').genericGetById;
|
||||
const imageDimensionValidator = require('../middleware/validators').imageDimensionValidator;
|
||||
const MAX_IMAGE_WIDTH = 150;
|
||||
const MAX_IMAGE_HEIGHT = 150;
|
||||
|
||||
const squads = new express.Router();
|
||||
|
||||
|
@ -54,6 +57,12 @@ squads.route('/')
|
|||
const squad = new SquadModel(req.body);
|
||||
// timestamp and default are set automatically by Mongoose Schema Validation
|
||||
if (req.file) {
|
||||
const imageFileBuffer = req.file.buffer;
|
||||
const err = imageDimensionValidator(imageFileBuffer, MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT);
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
squad.save((err) => {
|
||||
if (err) {
|
||||
err.status = codes.wrongrequest;
|
||||
|
@ -62,8 +71,7 @@ squads.route('/')
|
|||
}
|
||||
res.status(codes.created);
|
||||
res.locals.items = squad;
|
||||
fs.appendFile(resourceLocation.concat(squad._id).concat('.png'),
|
||||
new Buffer(req.file.buffer), (err) => {
|
||||
fs.appendFile(resourceLocation.concat(squad._id).concat('.png'), new Buffer(imageFileBuffer), (err) => {
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
|
@ -98,11 +106,16 @@ squads.route('/:id')
|
|||
req.body.$inc = {__v: 1};
|
||||
|
||||
if (req.file) {
|
||||
const imageFileBuffer = req.file.buffer;
|
||||
const err = imageDimensionValidator(imageFileBuffer, MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT);
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
const file = resourceLocation.concat(req.params.id)
|
||||
.concat('.png');
|
||||
fs.unlink(file, (err) => {
|
||||
if (err) next(err);
|
||||
fs.appendFile(file, new Buffer(req.file.buffer), (err) => {
|
||||
fs.appendFile(file, new Buffer(imageFileBuffer), (err) => {
|
||||
if (err) next(err);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -66,7 +66,7 @@ export class EditDecorationComponent implements OnInit, OnDestroy {
|
|||
if (this.fileList) {
|
||||
file = this.fileList[0];
|
||||
this.decorationService.submitDecoration(this.decoration, file)
|
||||
.subscribe(rank => {
|
||||
.subscribe(decoration => {
|
||||
this.router.navigate(['..'], {relativeTo: this.route});
|
||||
});
|
||||
} else {
|
||||
|
@ -83,12 +83,15 @@ export class EditDecorationComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
delete this.decoration['__v'];
|
||||
this.decorationService.submitDecoration(this.decoration, file)
|
||||
.subscribe(rank => {
|
||||
.subscribe(decoration => {
|
||||
setTimeout(() => {
|
||||
this.imagePreviewSrc = 'resource/decoration/' + this.decoration._id + '.png?' + Date.now();
|
||||
}, 300);
|
||||
fileInput.value = '';
|
||||
this.snackBarService.showSuccess('generic.save.success');
|
||||
}, error => {
|
||||
const errorMsg = error._body ? JSON.parse(error._body).error.message : error.error.error.message;
|
||||
this.snackBarService.showError('Error: '.concat(errorMsg), 15000);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ export class EditSquadComponent implements OnInit, OnDestroy {
|
|||
if (this.fileList) {
|
||||
file = this.fileList[0];
|
||||
this.squadService.submitSquad(this.squad, file)
|
||||
.subscribe(rank => {
|
||||
.subscribe(squad => {
|
||||
this.saved = true;
|
||||
this.router.navigate(['..'], {relativeTo: this.route});
|
||||
});
|
||||
|
@ -87,12 +87,15 @@ export class EditSquadComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
delete this.squad['__v'];
|
||||
this.squadService.submitSquad(this.squad, file)
|
||||
.subscribe(rank => {
|
||||
.subscribe(squad => {
|
||||
setTimeout(() => {
|
||||
this.imagePreviewSrc = 'resource/squad/' + this.squad._id + '.png?' + Date.now();
|
||||
}, 300);
|
||||
fileInput.value = '';
|
||||
this.snackBarService.showSuccess('generic.save.success');
|
||||
}, error => {
|
||||
const errorMsg = error._body ? JSON.parse(error._body).error.message : error.error.error.message;
|
||||
this.snackBarService.showError('Error: '.concat(errorMsg), 15000);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue