The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
Rocket.Chat/packages/rocketchat-file-upload/server/lib/FileUpload.js

306 lines
7.6 KiB

/* globals UploadFS */
import fs from 'fs';
import stream from 'stream';
import mime from 'mime-type/with-db';
import Future from 'fibers/future';
import sharp from 'sharp';
Object.assign(FileUpload, {
handlers: {},
configureUploadsStore(store, name, options) {
const type = name.split(':').pop();
const stores = UploadFS.getStores();
delete stores[name];
return new UploadFS.store[store](Object.assign({
name
}, options, FileUpload[`default${ type }`]()));
},
defaultUploads() {
return {
collection: RocketChat.models.Uploads.model,
filter: new UploadFS.Filter({
onCheck: FileUpload.validateFileUpload
}),
getPath(file) {
return `${ RocketChat.settings.get('uniqueID') }/uploads/${ file.rid }/${ file.userId }/${ file._id }`;
},
// transformWrite: FileUpload.uploadsTransformWrite
onValidate: FileUpload.uploadsOnValidate
};
},
defaultAvatars() {
return {
collection: RocketChat.models.Avatars.model,
// filter: new UploadFS.Filter({
// onCheck: FileUpload.validateFileUpload
// }),
// transformWrite: FileUpload.avatarTransformWrite,
getPath(file) {
return `${ RocketChat.settings.get('uniqueID') }/avatars/${ file.userId }`;
},
onValidate: FileUpload.avatarsOnValidate,
onFinishUpload: FileUpload.avatarsOnFinishUpload
};
},
avatarTransformWrite(readStream, writeStream/*, fileId, file*/) {
if (RocketChatFile.enabled === false || RocketChat.settings.get('Accounts_AvatarResize') !== true) {
return readStream.pipe(writeStream);
}
const height = RocketChat.settings.get('Accounts_AvatarSize');
const width = height;
return (file => RocketChat.Info.GraphicsMagick.enabled ? file: file.alpha('remove'))(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 tempFilePath = UploadFS.getTempFilePath(file._id);
const height = RocketChat.settings.get('Accounts_AvatarSize');
const width = height;
const future = new Future();
(file => RocketChat.Info.GraphicsMagick.enabled ? file: file.alpha('remove'))(RocketChatFile.gm(tempFilePath).background('#FFFFFF')).resize(width, `${ height }^`).gravity('Center').crop(width, height).extent(width, height).setFormat('jpeg').write(tempFilePath, Meteor.bindEnvironment(err => {
if (err != null) {
console.error(err);
}
const size = fs.lstatSync(tempFilePath).size;
this.getCollection().direct.update({_id: file._id}, {$set: {size}});
future.return();
}));
return future.wait();
},
resizeImagePreview(file, callback) {
const image = FileUpload.getStore('Uploads')._store.getReadStream(file._id, file);
const transformer = sharp().resize(50, 50).max().toBuffer(function(err, out) {
if (err) { throw err; }
callback(out.toString('base64'));
});
if (/^image\/.+/.test(file.type)) {
image.pipe(transformer);
} else {
callback();
}
},
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) {
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;
}
const ext = mime.extension(file.type);
if (ext && false === new RegExp(`\.${ ext }$`, 'i').test(file.name)) {
file.name = `${ file.name }.${ ext }`;
}
return file;
},
getStore(modelName) {
const storageType = RocketChat.settings.get('FileUpload_Storage_Type');
const handlerName = `${ storageType }:${ modelName }`;
return this.getStoreByName(handlerName);
},
getStoreByName(handlerName) {
if (this.handlers[handlerName] == null) {
console.error(`Upload handler "${ handlerName }" does not exists`);
}
return this.handlers[handlerName];
},
get(file, req, res, next) {
const store = this.getStoreByName(file.store);
if (store && store.get) {
return store.get(file, req, res, next);
}
res.writeHead(404);
res.end();
}
});
export class FileUploadClass {
constructor({ name, model, store, get, insert, getStore }) {
this.name = name;
this.model = model || this.getModelFromName();
this._store = store || UploadFS.getStore(name);
this.get = get;
if (insert) {
this.insert = insert;
}
if (getStore) {
this.getStore = getStore;
}
FileUpload.handlers[name] = this;
}
getStore() {
return this._store;
}
get store() {
return this.getStore();
}
set store(store) {
this._store = store;
}
getModelFromName() {
return RocketChat.models[this.name.split(':')[1]];
}
delete(fileId) {
if (this.store && this.store.delete) {
this.store.delete(fileId);
}
return this.model.deleteFile(fileId);
}
deleteById(fileId) {
const file = this.model.findOneById(fileId);
if (!file) {
return;
}
const store = FileUpload.getStoreByName(file.store);
return store.delete(file._id);
}
deleteByName(fileName) {
const file = this.model.findOneByName(fileName);
if (!file) {
return;
}
const store = FileUpload.getStoreByName(file.store);
return store.delete(file._id);
}
insert(fileData, streamOrBuffer, cb) {
const fileId = this.store.create(fileData);
const token = this.store.createToken(fileId);
const tmpFile = UploadFS.getTempFilePath(fileId);
try {
if (streamOrBuffer instanceof stream) {
streamOrBuffer.pipe(fs.createWriteStream(tmpFile));
} else if (streamOrBuffer instanceof Buffer) {
fs.writeFileSync(tmpFile, streamOrBuffer);
} else {
throw new Error('Invalid file type');
}
const file = Meteor.call('ufsComplete', fileId, this.name, token);
if (cb) {
cb(null, file);
}
return file;
} catch (e) {
if (cb) {
cb(e);
} else {
throw e;
}
}
}
}