From 2b1e35e55f63a2d4379584724a41afce79fc9573 Mon Sep 17 00:00:00 2001 From: lefuturiste Date: Wed, 29 Jul 2020 20:12:23 +0000 Subject: [PATCH] feat: add whole JSON size validation and check for storage usage --- src/MediaService.ts | 27 ++++++++++++ src/app.ts | 1 + .../AdminOrganizationController.ts | 2 +- src/controllers/DelegateController.ts | 42 +++++++++++++++++++ src/models/Organization.ts | 2 +- 5 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/MediaService.ts b/src/MediaService.ts index f90007c..866740f 100644 --- a/src/MediaService.ts +++ b/src/MediaService.ts @@ -1,3 +1,4 @@ +import Utils from './Utils' import aws from 'aws-sdk' import multer from 'multer' import multerS3 from 'multer-s3' @@ -79,4 +80,30 @@ export default class MediaService { static getMediaBaseUrl() { return process.env.S3_BASE_URL == null ? '___BUCKET_BASE_URL_NOT_FOUND_ENV_VAR_ISSUE___' : process.env.S3_BASE_URL } + + static getSizeLimit(): number { + //return 1073741824 for 1 GB + return 1000000000 + } + + // will return the sum of all the medias size used by the proposedVersion of the organization + static computeSize(version: any) { + // if (!Utils.isUsable(org, 'proposedVersion')) { + // return 0 + // } + // let version: any = org.proposedVersion + let size: number = 0 + if (Utils.isUsable(version, 'thumbnail.size')) { + size += version.thumbnail.size + } + if (Utils.isUsable(version, 'cover.size')) { + size += version.cover.size + } + if (Utils.isUsable(version, 'gallery') && Array.isArray(version.gallery)) { + version.gallery.forEach((media: any) => { + size += media.size + }) + } + return size + } } \ No newline at end of file diff --git a/src/app.ts b/src/app.ts index da87029..017c5d3 100644 --- a/src/app.ts +++ b/src/app.ts @@ -91,6 +91,7 @@ let main = async () => { app.use('/delegate', express.Router() .use(DelegateAuthMiddleware.handle) .get('/', DelegateController.get) + .get('/size', DelegateController.getCurrentSize) //.post('/test', DelegateController.test) .put('/', DelegateController.update) .post('/submit', DelegateController.submit) diff --git a/src/controllers/AdminOrganizationController.ts b/src/controllers/AdminOrganizationController.ts index 473e98c..202871f 100644 --- a/src/controllers/AdminOrganizationController.ts +++ b/src/controllers/AdminOrganizationController.ts @@ -33,7 +33,7 @@ export default class AdminOrganizationController { _note: 'Invalid import data or csv' }) } - let promises = parsed.data.map(d => { + let promises = parsed.data.map((d: any) => { return AdminOrganizationController.storeUniversal( d[0], d[1].replace(' ', ''), diff --git a/src/controllers/DelegateController.ts b/src/controllers/DelegateController.ts index 1ce7852..a65fcbd 100644 --- a/src/controllers/DelegateController.ts +++ b/src/controllers/DelegateController.ts @@ -21,6 +21,17 @@ export default class DelegateController { }) } + static getCurrentSize(req: express.Request, res: express.Response) { + res.json({ + success: true, + data: { + size: MediaService.computeSize(res.locals.organization.proposedVersion), + max: MediaService.getSizeLimit() + } + }) + } + + // static test(req: express.Request, res: express.Response) { // res.json({ success: true, body: req.body, organization: res.locals.organization }) // } @@ -103,6 +114,14 @@ export default class DelegateController { proposedVersion.pricing = [] } + // just before updating the whole organization, we want to check the size of the proposedVersion JSON String, just in case it is too large + if (JSON.stringify(proposedVersion).length > 10000) { + return res.status(413).json({ + success: false, + errors: [{ code: 'too-large', message: 'The proposedVersion object that you are trying to update is too long, we cannot handle it' }] + }) + } + Organization.updateOne({ _id: organization._id }, { proposedVersion, updatedAt: new Date() @@ -173,8 +192,29 @@ export default class DelegateController { }).catch(err => res.status(400).json({ success: false, errors: err.errors !== undefined ? err.errors : err })) } + static verifySize(req: express.Request, res: express.Response, next: any) { + // before starting the upload process, we want to make sure that this organization is not exeeding the size limit for all the file + let currentSize = MediaService.computeSize(res.locals.organization.proposedVersion) + if (currentSize >= MediaService.getSizeLimit()) { + return res + .status(413) + .json({ + success: false, + errors: [ + { code: 'quota-exeeded', message: 'We cannot accept this file because you already reached the maximum storage capacity for this organization' } + ], + data: { + currentSize, + maxSize: MediaService.getSizeLimit() + } + }) + } + next() + } + static uploadThumbnail(): express.RequestHandler[] { return [ + DelegateController.verifySize, MediaService.multer('thumbnail'), (req: express.Request, res: express.Response) => { // if the current thumbnail is defined AND the published thumnbnail is defined AND the current thumbnail is different from the published one @@ -202,6 +242,7 @@ export default class DelegateController { static uploadCover(): express.RequestHandler[] { return [ + DelegateController.verifySize, MediaService.multer('cover'), (req: express.Request, res: express.Response) => { // if the current thumbnail is defined AND the published thumnbnail is defined AND the current thumbnail is different from the published one @@ -226,6 +267,7 @@ export default class DelegateController { static uploadMedias(): express.RequestHandler[] { return [ + DelegateController.verifySize, MediaService.multer('media', true), (req: express.Request, res: express.Response) => { let proposedVersion: any = res.locals.organization.proposedVersion diff --git a/src/models/Organization.ts b/src/models/Organization.ts index 63b27a8..6dfd16a 100644 --- a/src/models/Organization.ts +++ b/src/models/Organization.ts @@ -32,7 +32,7 @@ const email = { lowercase: true, unique: true, validate: { - validator: function(v) { + validator: function(v: string) { return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(v); }, message: "Invalid email"