From 0c237b19fdf9c614293c1442f0ab95a81ce05735 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 22 Jun 2018 15:42:55 +0200 Subject: [PATCH] Fix images size limit --- .../my-account-settings.component.ts | 6 ++- client/src/app/shared/users/user.model.ts | 7 +++ .../shared/video-image.component.ts | 11 ++++- server/helpers/custom-validators/misc.ts | 4 ++ server/helpers/custom-validators/users.ts | 3 +- server/helpers/custom-validators/videos.ts | 4 +- server/middlewares/validators/users.ts | 10 +---- server/middlewares/validators/videos.ts | 45 ++++++++++--------- server/tests/api/check-params/videos.ts | 8 ++-- 9 files changed, 59 insertions(+), 39 deletions(-) diff --git a/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts b/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts index 15f977e58..14293f14c 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-settings.component.ts @@ -50,6 +50,10 @@ export class MyAccountSettingsComponent implements OnInit { changeAvatar () { const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ] + if (avatarfile.size > this.maxAvatarSize) { + this.notificationsService.error('Error', 'This image is too large.') + return + } const formData = new FormData() formData.append('avatarfile', avatarfile) @@ -59,7 +63,7 @@ export class MyAccountSettingsComponent implements OnInit { data => { this.notificationsService.success(this.i18n('Success'), this.i18n('Avatar changed.')) - this.user.account.avatar = data.avatar + this.user.updateAccountAvatar(data.avatar) }, err => this.notificationsService.error(this.i18n('Error'), err.message) diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts index b4be2270f..60a0f26df 100644 --- a/client/src/app/shared/users/user.model.ts +++ b/client/src/app/shared/users/user.model.ts @@ -9,6 +9,7 @@ import { import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type' import { Actor } from '@app/shared/actor/actor.model' import { Account } from '@app/shared/account/account.model' +import { Avatar } from '../../../../../shared/models/avatars/avatar.model' export type UserConstructorHash = { id: number, @@ -84,6 +85,12 @@ export class User implements UserServerModel { this.updateComputedAttributes() } + updateAccountAvatar (newAccountAvatar: Avatar) { + this.account.avatar = newAccountAvatar + + this.updateComputedAttributes() + } + private updateComputedAttributes () { this.accountAvatarUrl = Actor.GET_ACTOR_AVATAR_URL(this.account) } diff --git a/client/src/app/videos/+video-edit/shared/video-image.component.ts b/client/src/app/videos/+video-edit/shared/video-image.component.ts index df6565857..25955baaa 100644 --- a/client/src/app/videos/+video-edit/shared/video-image.component.ts +++ b/client/src/app/videos/+video-edit/shared/video-image.component.ts @@ -2,6 +2,8 @@ import { Component, forwardRef, Input } from '@angular/core' import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' import { ServerService } from '@app/core' +import { NotificationsService } from 'angular2-notifications' +import { I18n } from '@ngx-translate/i18n-polyfill' @Component({ selector: 'my-video-image', @@ -27,7 +29,9 @@ export class VideoImageComponent implements ControlValueAccessor { constructor ( private sanitizer: DomSanitizer, - private serverService: ServerService + private serverService: ServerService, + private notificationsService: NotificationsService, + private i18n: I18n ) {} get videoImageExtensions () { @@ -42,6 +46,11 @@ export class VideoImageComponent implements ControlValueAccessor { if (event.target.files && event.target.files.length) { const [ file ] = event.target.files + if (file.size > this.maxVideoImageSize) { + this.notificationsService.error(this.i18n('Error'), this.i18n('This image is too large.')) + return + } + this.file = file this.propagateChange(this.file) this.updatePreview() diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts index 254b4db6c..455aae367 100644 --- a/server/helpers/custom-validators/misc.ts +++ b/server/helpers/custom-validators/misc.ts @@ -45,6 +45,7 @@ function isFileValid ( files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], mimeTypeRegex: string, field: string, + maxSize: number, optional = false ) { // Should have files @@ -61,6 +62,9 @@ function isFileValid ( const file = fileArray[ 0 ] if (!file || !file.originalname) return false + // Check size + if (maxSize && file.size > maxSize) return false + return new RegExp(`^${mimeTypeRegex}$`, 'i').test(file.mimetype) } diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts index b59b766de..ce1323e94 100644 --- a/server/helpers/custom-validators/users.ts +++ b/server/helpers/custom-validators/users.ts @@ -2,7 +2,6 @@ import 'express-validator' import * as validator from 'validator' import { UserRole } from '../../../shared' import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers' - import { exists, isFileValid } from './misc' import { values } from 'lodash' @@ -52,7 +51,7 @@ const avatarMimeTypes = CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME .join('|') const avatarMimeTypesRegex = `image/(${avatarMimeTypes})` function isAvatarFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) { - return isFileValid(files, avatarMimeTypesRegex, 'avatarfile') + return isFileValid(files, avatarMimeTypesRegex, 'avatarfile', CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max) } // --------------------------------------------------------------------------- diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts index a227136ac..ae392f8c2 100644 --- a/server/helpers/custom-validators/videos.ts +++ b/server/helpers/custom-validators/videos.ts @@ -86,7 +86,7 @@ const videoFileTypes = Object.keys(VIDEO_MIMETYPE_EXT).map(m => `(${m})`) const videoFileTypesRegex = videoFileTypes.join('|') function isVideoFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) { - return isFileValid(files, videoFileTypesRegex, 'videofile') + return isFileValid(files, videoFileTypesRegex, 'videofile', null) } const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME @@ -95,7 +95,7 @@ const videoImageTypes = CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME const videoImageTypesRegex = `image/(${videoImageTypes})` function isVideoImage (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[], field: string) { - return isFileValid(files, videoImageTypesRegex, field, true) + return isFileValid(files, videoImageTypesRegex, field, CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max, true) } function isVideoPrivacyValid (value: number) { diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 8fbab4dd0..55a08a648 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts @@ -118,7 +118,7 @@ const usersUpdateMeValidator = [ const usersUpdateMyAvatarValidator = [ body('avatarfile').custom((value, { req }) => isAvatarFile(req.files)).withMessage( - 'This file is not supported. Please, make sure it is of the following type : ' + 'This file is not supported or too large. Please, make sure it is of the following type : ' + CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME.join(', ') ), @@ -127,14 +127,6 @@ const usersUpdateMyAvatarValidator = [ if (areValidationErrors(req, res)) return - const imageFile = req.files['avatarfile'][0] as Express.Multer.File - if (imageFile.size > CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max) { - res.status(400) - .send({ error: `The size of the avatar is too big (>${CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max}).` }) - .end() - return - } - return next() } ] diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index 5595edf17..59d65d5a4 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts @@ -38,18 +38,21 @@ import { authenticate } from '../oauth' import { areValidationErrors } from './utils' const videosAddValidator = [ - body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( - 'This file is not supported. Please, make sure it is of the following type : ' - + CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') - ), - body('thumbnailfile').custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage( - 'This thumbnail file is not supported. Please, make sure it is of the following type : ' - + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') - ), - body('previewfile').custom((value, { req }) => isVideoImage(req.files, 'previewfile')).withMessage( - 'This preview file is not supported. Please, make sure it is of the following type : ' - + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') - ), + body('videofile') + .custom((value, { req }) => isVideoFile(req.files)).withMessage( + 'This file is not supported or too large. Please, make sure it is of the following type : ' + + CONSTRAINTS_FIELDS.VIDEOS.EXTNAME.join(', ') + ), + body('thumbnailfile') + .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage( + 'This thumbnail file is not supported or too large. Please, make sure it is of the following type : ' + + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') + ), + body('previewfile') + .custom((value, { req }) => isVideoImage(req.files, 'previewfile')).withMessage( + 'This preview file is not supported or too large. Please, make sure it is of the following type : ' + + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') + ), body('name').custom(isVideoNameValid).withMessage('Should have a valid name'), body('category') .optional() @@ -147,14 +150,16 @@ const videosAddValidator = [ const videosUpdateValidator = [ param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), - body('thumbnailfile').custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage( - 'This thumbnail file is not supported. Please, make sure it is of the following type : ' - + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') - ), - body('previewfile').custom((value, { req }) => isVideoImage(req.files, 'previewfile')).withMessage( - 'This preview file is not supported. Please, make sure it is of the following type : ' - + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') - ), + body('thumbnailfile') + .custom((value, { req }) => isVideoImage(req.files, 'thumbnailfile')).withMessage( + 'This thumbnail file is not supported or too large. Please, make sure it is of the following type : ' + + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') + ), + body('previewfile') + .custom((value, { req }) => isVideoImage(req.files, 'previewfile')).withMessage( + 'This preview file is not supported or too large. Please, make sure it is of the following type : ' + + CONSTRAINTS_FIELDS.VIDEOS.IMAGE.EXTNAME.join(', ') + ), body('name') .optional() .custom(isVideoNameValid).withMessage('Should have a valid name'), diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts index abbea6ba3..7fce8ba7c 100644 --- a/server/tests/api/check-params/videos.ts +++ b/server/tests/api/check-params/videos.ts @@ -326,7 +326,7 @@ describe('Test videos API validator', function () { const fields = baseCorrectParams const attaches = { 'thumbnailfile': join(__dirname, '..', '..', 'fixtures', 'avatar.png'), - 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short_fake.webm') + 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short.mp4') } await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) @@ -336,7 +336,7 @@ describe('Test videos API validator', function () { const fields = baseCorrectParams const attaches = { 'thumbnailfile': join(__dirname, '..', '..', 'fixtures', 'avatar-big.png'), - 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short_fake.webm') + 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short.mp4') } await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) @@ -346,7 +346,7 @@ describe('Test videos API validator', function () { const fields = baseCorrectParams const attaches = { 'previewfile': join(__dirname, '..', '..', 'fixtures', 'avatar.png'), - 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short_fake.webm') + 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short.mp4') } await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) @@ -356,7 +356,7 @@ describe('Test videos API validator', function () { const fields = baseCorrectParams const attaches = { 'previewfile': join(__dirname, '..', '..', 'fixtures', 'avatar-big.png'), - 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short_fake.webm') + 'videofile': join(__dirname, '..', '..', 'fixtures', 'video_short.mp4') } await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })