From 75b0a1969ff2281cf9c701b15fd0697ca31a53b2 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 25 May 2017 18:53:23 -0300 Subject: [PATCH] Move GridFS to uploads package and resuse more code --- lib/fileUpload.js | 136 ------------------ .../client/lib/FileUploadGridFS.js | 5 + packages/rocketchat-file-upload/package.js | 1 + .../server/config/configFileUploadAmazonS3.js | 85 +---------- .../config/configFileUploadFileSystem.js | 45 +----- .../config/configFileUploadGoogleStorage.js | 84 +---------- .../server/config/configFileUploadGridFS.js | 61 ++++++++ .../server/lib/FileUpload.js | 106 ++++++++++++++ 8 files changed, 181 insertions(+), 342 deletions(-) delete mode 100644 lib/fileUpload.js diff --git a/lib/fileUpload.js b/lib/fileUpload.js deleted file mode 100644 index 3ba8109a3c2..00000000000 --- a/lib/fileUpload.js +++ /dev/null @@ -1,136 +0,0 @@ -/* globals UploadFS, FileUpload */ -import { Cookies } from 'meteor/ostrio:cookies'; - -if (UploadFS) { - const initFileStore = function() { - const cookie = new Cookies(); - if (Meteor.isClient) { - document.cookie = `rc_uid=${ escape(Meteor.userId()) }; path=/`; - document.cookie = `rc_token=${ escape(Accounts._storedLoginToken()) }; path=/`; - } - - Meteor.fileStore = new UploadFS.store.GridFS({ - collection: RocketChat.models.Uploads.model, - name: 'GridFS:Uploads', - collectionName: 'rocketchat_uploads', - filter: new UploadFS.Filter({ - onCheck: FileUpload.validateFileUpload - }), - transformWrite(readStream, writeStream, fileId, file) { - if (RocketChatFile.enabled === false || !/^image\/.+/.test(file.type)) { - return readStream.pipe(writeStream); - } - - let stream = undefined; - - const identify = function(err, data) { - if (err) { - return stream.pipe(writeStream); - } - - file.identify = { - format: data.format, - size: data.size - }; - - if (data.Orientation && !['', 'Unknown', 'Undefined'].includes(data.Orientation)) { - RocketChatFile.gm(stream).autoOrient().stream().pipe(writeStream); - } else { - stream.pipe(writeStream); - } - }; - - stream = RocketChatFile.gm(readStream).identify(identify).stream(); - }, - - onRead(fileId, file, req, res) { - if (RocketChat.settings.get('FileUpload_ProtectFiles')) { - let uid; - let token; - - if (req && req.headers && req.headers.cookie) { - const rawCookies = req.headers.cookie; - - if (rawCookies) { - uid = cookie.get('rc_uid', rawCookies) ; - token = cookie.get('rc_token', rawCookies); - } - } - - if (!uid) { - uid = req.query.rc_uid; - token = req.query.rc_token; - } - - if (!uid || !token || !RocketChat.models.Users.findOneByIdAndLoginToken(uid, token)) { - res.writeHead(403); - return false; - } - } - - res.setHeader('content-disposition', `attachment; filename="${ encodeURIComponent(file.name) }"`); - return true; - } - }); - - // DEPRECATED: backwards compatibility (remove) - UploadFS.getStores()['rocketchat_uploads'] = UploadFS.getStores()['GridFS:Uploads']; - - Meteor.fileStoreAvatar = new UploadFS.store.GridFS({ - collection: RocketChat.models.Avatars.model, - name: 'GridFS:Avatars', - collectionName: 'rocketchat_avatars', - // filter: new UploadFS.Filter({ - // onCheck: FileUpload.validateFileUpload - // }), - transformWrite: FileUpload.avatarTransformWrite, - - onFinishUpload(file) { - // update file record to match user's username - const user = RocketChat.models.Users.findOneById(file.userId); - const oldAvatar = RocketChat.models.Avatars.findOneByName(user.username); - if (oldAvatar) { - Meteor.fileStoreAvatar.delete(oldAvatar._id); - RocketChat.models.Avatars.deleteFile(oldAvatar._id); - } - RocketChat.models.Avatars.updateFileNameById(file._id, user.username); - // console.log('upload finished ->', file); - }, - - onRead(fileId, file, req, res) { - if (RocketChat.settings.get('FileUpload_ProtectFiles')) { - let uid; - let token; - - if (req && req.headers && req.headers.cookie) { - const rawCookies = req.headers.cookie; - - if (rawCookies) { - uid = cookie.get('rc_uid', rawCookies) ; - token = cookie.get('rc_token', rawCookies); - } - } - - if (!uid) { - uid = req.query.rc_uid; - token = req.query.rc_token; - } - - if (!uid || !token || !RocketChat.models.Users.findOneByIdAndLoginToken(uid, token)) { - res.writeHead(403); - return false; - } - } - - res.setHeader('content-disposition', `attachment; filename="${ encodeURIComponent(file.name) }"`); - return true; - } - }); - }; - - Meteor.startup(function() { - if (Meteor.isServer) { - initFileStore(); - } - }); -} diff --git a/packages/rocketchat-file-upload/client/lib/FileUploadGridFS.js b/packages/rocketchat-file-upload/client/lib/FileUploadGridFS.js index 8e784207399..a69fef62a04 100644 --- a/packages/rocketchat-file-upload/client/lib/FileUploadGridFS.js +++ b/packages/rocketchat-file-upload/client/lib/FileUploadGridFS.js @@ -16,3 +16,8 @@ new UploadFS.store.GridFS({ onCheck: FileUpload.validateFileUpload }) }); + +Tracker.autorun(function() { + document.cookie = `rc_uid=${ escape(Meteor.userId()) }; path=/`; + document.cookie = `rc_token=${ escape(Accounts._storedLoginToken()) }; path=/`; +}); diff --git a/packages/rocketchat-file-upload/package.js b/packages/rocketchat-file-upload/package.js index 09901093207..4c4d2df7cda 100644 --- a/packages/rocketchat-file-upload/package.js +++ b/packages/rocketchat-file-upload/package.js @@ -18,6 +18,7 @@ Package.onUse(function(api) { api.use('peerlibrary:aws-sdk'); api.use('rocketchat:lib'); api.use('random'); + api.use('accounts-base'); api.use('underscore'); api.use('tracker'); api.use('webapp'); diff --git a/packages/rocketchat-file-upload/server/config/configFileUploadAmazonS3.js b/packages/rocketchat-file-upload/server/config/configFileUploadAmazonS3.js index 53f8760c851..2cb8be50254 100644 --- a/packages/rocketchat-file-upload/server/config/configFileUploadAmazonS3.js +++ b/packages/rocketchat-file-upload/server/config/configFileUploadAmazonS3.js @@ -1,11 +1,8 @@ /* globals FileUpload, UploadFS, RocketChatFile */ -import fs from 'fs'; import { FileUploadClass } from '../lib/FileUpload'; import '../../ufs/AmazonS3/server.js'; -const Future = Npm.require('fibers/future'); - const insert = function(file, stream, cb) { const fileId = this.store.create(file); @@ -38,46 +35,6 @@ const AmazonS3Avatars = new FileUploadClass({ insert }); -const onValidate = function(file) { - if (RocketChatFile.enabled === false || !/^image\/((x-windows-)?bmp|p?jpeg|png)$/.test(file.type)) { - return; - } - - const tmpFile = UploadFS.getTempFilePath(file._id); - - const fut = new Future(); - - const identify = Meteor.bindEnvironment((err, data) => { - if (err != null) { - console.error(err); - return fut.return(); - } - - file.identify = { - format: data.format, - size: data.size - }; - - if ([null, undefined, '', 'Unknown', 'Undefined'].includes(data.Orientation)) { - return fut.return(); - } - - RocketChatFile.gm(tmpFile).autoOrient().write(tmpFile, Meteor.bindEnvironment((err) => { - if (err != null) { - console.error(err); - } - - const size = fs.lstatSync(tmpFile).size; - this.getCollection().direct.update({_id: file._id}, {$set: {size}}); - fut.return(); - })); - }); - - RocketChatFile.gm(tmpFile).identify(identify); - - return fut.wait(); -}; - const configure = _.debounce(function() { const stores = UploadFS.getStores(); delete stores[AmazonS3Uploads.name]; @@ -112,50 +69,14 @@ const configure = _.debounce(function() { onCheck: FileUpload.validateFileUpload }), name: AmazonS3Uploads.name, - onValidate + onValidate: FileUpload.uploadsOnValidate }, config)); AmazonS3Avatars.store = new UploadFS.store.AmazonS3(Object.assign({ collection: AmazonS3Avatars.model.model, name: AmazonS3Avatars.name, - onFinishUpload(file) { - // update file record to match user's username - const user = RocketChat.models.Users.findOneById(file.userId); - const oldAvatar = AmazonS3Avatars.model.findOneByName(user.username); - if (oldAvatar) { - try { - AmazonS3Avatars.deleteById(oldAvatar._id); - } catch (e) { - console.error(e); - } - } - AmazonS3Avatars.model.updateFileNameById(file._id, user.username); - // console.log('upload finished ->', file); - }, - onValidate(file) { - if (RocketChatFile.enabled === false || RocketChat.settings.get('Accounts_AvatarResize') !== true) { - return; - } - - const tmpFile = UploadFS.getTempFilePath(file._id); - - const fut = new Future(); - - const height = RocketChat.settings.get('Accounts_AvatarSize'); - const width = height; - - RocketChatFile.gm(tmpFile).background('#ffffff').resize(width, `${ height }^`).gravity('Center').crop(width, height).extent(width, height).setFormat('jpeg').write(tmpFile, Meteor.bindEnvironment((err) => { - if (err != null) { - console.error(err); - } - - const size = fs.lstatSync(tmpFile).size; - this.getCollection().direct.update({_id: file._id}, {$set: {size}}); - fut.return(); - })); - - return fut.wait(); - } + onFinishUpload: FileUpload.avatarsOnFinishUpload, + onValidate: FileUpload.avatarsOnValidate }, config)); }, 500); diff --git a/packages/rocketchat-file-upload/server/config/configFileUploadFileSystem.js b/packages/rocketchat-file-upload/server/config/configFileUploadFileSystem.js index 680019a966b..664f23661b8 100644 --- a/packages/rocketchat-file-upload/server/config/configFileUploadFileSystem.js +++ b/packages/rocketchat-file-upload/server/config/configFileUploadFileSystem.js @@ -78,34 +78,6 @@ const FileSystemAvatars = new FileUploadClass({ }); -const transformWrite = function(readStream, writeStream, fileId, file) { - if (RocketChatFile.enabled === false || !/^image\/((x-windows-)?bmp|p?jpeg|png)$/.test(file.type)) { - return readStream.pipe(writeStream); - } - - let stream = undefined; - - const identify = function(err, data) { - if (err != null) { - return stream.pipe(writeStream); - } - - file.identify = { - format: data.format, - size: data.size - }; - - if ([null, undefined, '', 'Unknown', 'Undefined'].indexOf(data.Orientation) === -1) { - return RocketChatFile.gm(stream).autoOrient().stream().pipe(writeStream); - } else { - return stream.pipe(writeStream); - } - }; - - stream = RocketChatFile.gm(readStream).identify(identify).stream(); - return; -}; - const createFileSystemStore = _.debounce(function() { const stores = UploadFS.getStores(); delete stores['FileSystem:Uploads']; @@ -118,7 +90,7 @@ const createFileSystemStore = _.debounce(function() { onCheck: FileUpload.validateFileUpload }), name: FileSystemUploads.name, - transformWrite + transformWrite: FileUpload.uploadsTransformWrite }); UploadFS.getStores()['fileSystem'] = UploadFS.getStores()[FileSystemUploads.name]; @@ -128,20 +100,7 @@ const createFileSystemStore = _.debounce(function() { collection: FileSystemAvatars.model.model, name: FileSystemAvatars.name, transformWrite: FileUpload.avatarTransformWrite, - onFinishUpload(file) { - // update file record to match user's username - const user = RocketChat.models.Users.findOneById(file.userId); - const oldAvatar = FileSystemAvatars.model.findOneByName(user.username); - if (oldAvatar) { - try { - FileSystemAvatars.deleteById(oldAvatar._id); - } catch (e) { - console.error(e); - } - } - FileSystemAvatars.model.updateFileNameById(file._id, user.username); - // console.log('upload finished ->', file); - } + onFinishUpload: FileUpload.avatarsOnFinishUpload }); }, 500); diff --git a/packages/rocketchat-file-upload/server/config/configFileUploadGoogleStorage.js b/packages/rocketchat-file-upload/server/config/configFileUploadGoogleStorage.js index 7e48b338183..3e041960c97 100644 --- a/packages/rocketchat-file-upload/server/config/configFileUploadGoogleStorage.js +++ b/packages/rocketchat-file-upload/server/config/configFileUploadGoogleStorage.js @@ -1,10 +1,8 @@ /* globals FileUpload, UploadFS, RocketChatFile */ -import fs from 'fs'; import { FileUploadClass } from '../lib/FileUpload'; import '../../ufs/GoogleStorage/server.js'; -const Future = Npm.require('fibers/future'); const insert = function(file, stream, cb) { const fileId = this.store.create(file); @@ -42,46 +40,6 @@ const GoogleCloudStorageAvatars = new FileUploadClass({ insert }); -const onValidate = function(file) { - if (RocketChatFile.enabled === false || !/^image\/((x-windows-)?bmp|p?jpeg|png)$/.test(file.type)) { - return; - } - - const tmpFile = UploadFS.getTempFilePath(file._id); - - const fut = new Future(); - - const identify = Meteor.bindEnvironment((err, data) => { - if (err != null) { - console.error(err); - return fut.return(); - } - - file.identify = { - format: data.format, - size: data.size - }; - - if ([null, undefined, '', 'Unknown', 'Undefined'].includes(data.Orientation)) { - return fut.return(); - } - - RocketChatFile.gm(tmpFile).autoOrient().write(tmpFile, Meteor.bindEnvironment((err) => { - if (err != null) { - console.error(err); - } - - const size = fs.lstatSync(tmpFile).size; - this.getCollection().direct.update({_id: file._id}, {$set: {size}}); - fut.return(); - })); - }); - - RocketChatFile.gm(tmpFile).identify(identify); - - return fut.wait(); -}; - const configure = _.debounce(function() { const stores = UploadFS.getStores(); delete stores[GoogleCloudStorageUploads.name]; @@ -110,50 +68,14 @@ const configure = _.debounce(function() { onCheck: FileUpload.validateFileUpload }), name: GoogleCloudStorageUploads.name, - onValidate + onValidate: FileUpload.uploadsOnValidate }, config)); GoogleCloudStorageAvatars.store = new UploadFS.store.GoogleStorage(Object.assign({ collection: GoogleCloudStorageAvatars.model.model, name: GoogleCloudStorageAvatars.name, - onFinishUpload(file) { - // update file record to match user's username - const user = RocketChat.models.Users.findOneById(file.userId); - const oldAvatar = GoogleCloudStorageAvatars.model.findOneByName(user.username); - if (oldAvatar) { - try { - GoogleCloudStorageAvatars.deleteById(oldAvatar._id); - } catch (e) { - console.error(e); - } - } - GoogleCloudStorageAvatars.model.updateFileNameById(file._id, user.username); - // console.log('upload finished ->', file); - }, - onValidate(file) { - if (RocketChatFile.enabled === false || RocketChat.settings.get('Accounts_AvatarResize') !== true) { - return; - } - - const tmpFile = UploadFS.getTempFilePath(file._id); - - const fut = new Future(); - - const height = RocketChat.settings.get('Accounts_AvatarSize'); - const width = height; - - RocketChatFile.gm(tmpFile).background('#ffffff').resize(width, `${ height }^`).gravity('Center').crop(width, height).extent(width, height).setFormat('jpeg').write(tmpFile, Meteor.bindEnvironment((err) => { - if (err != null) { - console.error(err); - } - - const size = fs.lstatSync(tmpFile).size; - this.getCollection().direct.update({_id: file._id}, {$set: {size}}); - fut.return(); - })); - - return fut.wait(); - } + onFinishUpload: FileUpload.avatarsOnFinishUpload, + onValidate: FileUpload.avatarsOnValidate }, config)); }, 500); diff --git a/packages/rocketchat-file-upload/server/config/configFileUploadGridFS.js b/packages/rocketchat-file-upload/server/config/configFileUploadGridFS.js index 49d6b72e61b..93aac130643 100644 --- a/packages/rocketchat-file-upload/server/config/configFileUploadGridFS.js +++ b/packages/rocketchat-file-upload/server/config/configFileUploadGridFS.js @@ -4,6 +4,9 @@ import zlib from 'zlib'; import util from 'util'; import { FileUploadClass } from '../lib/FileUpload'; +import { Cookies } from 'meteor/ostrio:cookies'; + +const cookie = new Cookies(); const logger = new Logger('FileUpload'); @@ -127,6 +130,64 @@ const readFromGridFS = function(storeName, fileId, file, headers, req, res) { } }; +const onRead = function(fileId, file, req, res) { + if (RocketChat.settings.get('FileUpload_ProtectFiles')) { + let uid; + let token; + + if (req && req.headers && req.headers.cookie) { + const rawCookies = req.headers.cookie; + + if (rawCookies) { + uid = cookie.get('rc_uid', rawCookies) ; + token = cookie.get('rc_token', rawCookies); + } + } + + if (!uid) { + uid = req.query.rc_uid; + token = req.query.rc_token; + } + + if (!uid || !token || !RocketChat.models.Users.findOneByIdAndLoginToken(uid, token)) { + res.writeHead(403); + return false; + } + } + + res.setHeader('content-disposition', `attachment; filename="${ encodeURIComponent(file.name) }"`); + return true; +}; + +Meteor.fileStore = new UploadFS.store.GridFS({ + collection: RocketChat.models.Uploads.model, + name: 'GridFS:Uploads', + collectionName: 'rocketchat_uploads', + filter: new UploadFS.Filter({ + onCheck: FileUpload.validateFileUpload + }), + transformWrite: FileUpload.uploadsTransformWrite, + + onRead +}); + +// DEPRECATED: backwards compatibility (remove) +UploadFS.getStores()['rocketchat_uploads'] = UploadFS.getStores()['GridFS:Uploads']; + +Meteor.fileStoreAvatar = new UploadFS.store.GridFS({ + collection: RocketChat.models.Avatars.model, + name: 'GridFS:Avatars', + collectionName: 'rocketchat_avatars', + // filter: new UploadFS.Filter({ + // onCheck: FileUpload.validateFileUpload + // }), + transformWrite: FileUpload.avatarTransformWrite, + onFinishUpload: FileUpload.avatarsOnFinishUpload, + + onRead +}); + + const insert = function(file, stream, cb) { const fileId = this.store.create(file); diff --git a/packages/rocketchat-file-upload/server/lib/FileUpload.js b/packages/rocketchat-file-upload/server/lib/FileUpload.js index b3e616859c2..25684170756 100644 --- a/packages/rocketchat-file-upload/server/lib/FileUpload.js +++ b/packages/rocketchat-file-upload/server/lib/FileUpload.js @@ -1,6 +1,8 @@ /* globals UploadFS */ +import fs from 'fs'; import mime from 'mime-type/with-db'; +import Future from 'fibers/future'; Object.assign(FileUpload, { handlers: {}, @@ -14,6 +16,110 @@ Object.assign(FileUpload, { return RocketChatFile.gm(readStream).background('#ffffff').resize(width, `${ height }^`).gravity('Center').crop(width, height).extent(width, height).stream('jpeg').pipe(writeStream); }, + avatarsOnValidate(file) { + if (RocketChatFile.enabled === false || RocketChat.settings.get('Accounts_AvatarResize') !== true) { + return; + } + + const tmpFile = UploadFS.getTempFilePath(file._id); + + const fut = new Future(); + + const height = RocketChat.settings.get('Accounts_AvatarSize'); + const width = height; + + RocketChatFile.gm(tmpFile).background('#ffffff').resize(width, `${ height }^`).gravity('Center').crop(width, height).extent(width, height).setFormat('jpeg').write(tmpFile, Meteor.bindEnvironment((err) => { + if (err != null) { + console.error(err); + } + + const size = fs.lstatSync(tmpFile).size; + this.getCollection().direct.update({_id: file._id}, {$set: {size}}); + fut.return(); + })); + + return fut.wait(); + }, + + uploadsTransformWrite(readStream, writeStream, fileId, file) { + if (RocketChatFile.enabled === false || !/^image\/.+/.test(file.type)) { + return readStream.pipe(writeStream); + } + + let stream = undefined; + + const identify = function(err, data) { + if (err) { + return stream.pipe(writeStream); + } + + file.identify = { + format: data.format, + size: data.size + }; + + if (data.Orientation && !['', 'Unknown', 'Undefined'].includes(data.Orientation)) { + RocketChatFile.gm(stream).autoOrient().stream().pipe(writeStream); + } else { + stream.pipe(writeStream); + } + }; + + stream = RocketChatFile.gm(readStream).identify(identify).stream(); + }, + + uploadsOnValidate(file) { + if (RocketChatFile.enabled === false || !/^image\/((x-windows-)?bmp|p?jpeg|png)$/.test(file.type)) { + return; + } + + const tmpFile = UploadFS.getTempFilePath(file._id); + + const fut = new Future(); + + const identify = Meteor.bindEnvironment((err, data) => { + if (err != null) { + console.error(err); + return fut.return(); + } + + file.identify = { + format: data.format, + size: data.size + }; + + if ([null, undefined, '', 'Unknown', 'Undefined'].includes(data.Orientation)) { + return fut.return(); + } + + RocketChatFile.gm(tmpFile).autoOrient().write(tmpFile, Meteor.bindEnvironment((err) => { + if (err != null) { + console.error(err); + } + + const size = fs.lstatSync(tmpFile).size; + this.getCollection().direct.update({_id: file._id}, {$set: {size}}); + fut.return(); + })); + }); + + RocketChatFile.gm(tmpFile).identify(identify); + + return fut.wait(); + }, + + avatarsOnFinishUpload(file) { + // update file record to match user's username + const user = RocketChat.models.Users.findOneById(file.userId); + const oldAvatar = RocketChat.models.Avatars.findOneByName(user.username); + if (oldAvatar) { + this.delete(oldAvatar._id); + RocketChat.models.Avatars.deleteFile(oldAvatar._id); + } + RocketChat.models.Avatars.updateFileNameById(file._id, user.username); + // console.log('upload finished ->', file); + }, + addExtensionTo(file) { if (mime.lookup(file.name) === file.type) { return file;