Skip to content
Snippets Groups Projects
Unverified Commit 0a4c04d1 authored by Diego Sampaio's avatar Diego Sampaio
Browse files

Add S3 storage type to avatars

parent 193553c0
No related branches found
No related tags found
No related merge requests found
Showing
with 531 additions and 134 deletions
/* globals FileUpload, FileUploadBase, Slingshot */ /* globals FileUpload, FileUploadBase, Slingshot */
FileUpload.AmazonS3 = class FileUploadAmazonS3 extends FileUploadBase { FileUpload.AmazonS3 = class FileUploadAmazonS3 extends FileUploadBase {
constructor(meta, file) { constructor(directive, meta, file) {
super(meta, file); super(meta, file);
this.uploader = new Slingshot.Upload('rocketchat-uploads', { rid: meta.rid }); this.uploader = new Slingshot.Upload(directive, meta);
} }
start() { start(callback) {
this.uploader.send(this.file, (error, downloadUrl) => { this.uploader.send(this.file, (error, downloadUrl) => {
if (this.computation) { if (this.computation) {
this.computation.stop(); this.computation.stop();
} }
if (error) { if (error) {
let uploading = Session.get('uploading'); return callback.call(this, error);
if (!Array.isArray(uploading)) {
uploading = [];
}
const item = _.findWhere(uploading, { id: this.id });
if (_.isObject(item)) {
item.error = error.error;
item.percentage = 0;
} else {
uploading.push({
error: error.error,
percentage: 0
});
}
Session.set('uploading', uploading);
} else { } else {
const file = _.pick(this.meta, 'type', 'size', 'name', 'identify', 'description'); const file = _.pick(this.meta, 'type', 'size', 'name', 'identify', 'description');
file._id = downloadUrl.substr(downloadUrl.lastIndexOf('/') + 1); file._id = downloadUrl.substr(downloadUrl.lastIndexOf('/') + 1);
...@@ -38,13 +21,7 @@ FileUpload.AmazonS3 = class FileUploadAmazonS3 extends FileUploadBase { ...@@ -38,13 +21,7 @@ FileUpload.AmazonS3 = class FileUploadAmazonS3 extends FileUploadBase {
Meteor.call('sendFileMessage', this.meta.rid, 's3', file, () => { Meteor.call('sendFileMessage', this.meta.rid, 's3', file, () => {
Meteor.setTimeout(() => { Meteor.setTimeout(() => {
const uploading = Session.get('uploading'); callback.call(this, null, file);
if (uploading !== null) {
const item = _.findWhere(uploading, {
id: this.id
});
return Session.set('uploading', _.without(uploading, item));
}
}, 2000); }, 2000);
}); });
} }
......
/* globals FileUpload, fileUploadHandler:true */ /* globals FileUpload, fileUploadHandler:true */
/* exported fileUploadHandler */ /* exported fileUploadHandler */
fileUploadHandler = (meta, file) => { fileUploadHandler = (directive, meta, file) => {
const storageType = RocketChat.settings.get('FileUpload_Storage_Type'); const storageType = RocketChat.settings.get('FileUpload_Storage_Type');
if (FileUpload[storageType] !== undefined) { if (FileUpload[storageType] !== undefined) {
return new FileUpload[storageType](meta, file); return new FileUpload[storageType](directive, meta, file);
} }
}; };
...@@ -25,5 +25,6 @@ const slingShotConfig = { ...@@ -25,5 +25,6 @@ const slingShotConfig = {
allowedFileTypes: null allowedFileTypes: null
}; };
Slingshot.fileRestrictions('rocketchat-avatars', slingShotConfig);
Slingshot.fileRestrictions('rocketchat-uploads', slingShotConfig); Slingshot.fileRestrictions('rocketchat-uploads', slingShotConfig);
Slingshot.fileRestrictions('rocketchat-uploads-gs', slingShotConfig); Slingshot.fileRestrictions('rocketchat-uploads-gs', slingShotConfig);
/* globals Slingshot, FileUpload, AWS, SystemLogger */ /* globals Slingshot, FileUpload, AWS */
const crypto = Npm.require('crypto'); const crypto = Npm.require('crypto');
let S3accessKey; let S3accessKey;
...@@ -36,8 +36,81 @@ FileUpload.addHandler('s3', { ...@@ -36,8 +36,81 @@ FileUpload.addHandler('s3', {
} }
}); });
const createS3Directive = _.debounce(() => { function createDirective(directiveName, { key, bucket, accessKey, secretKey, region, acl, cdn, bucketUrl}) {
const directiveName = 'rocketchat-uploads'; if (Slingshot._directives[directiveName]) {
delete Slingshot._directives[directiveName];
}
const config = {
bucket,
key,
AWSAccessKeyId: accessKey,
AWSSecretAccessKey: secretKey
};
if (!_.isEmpty(acl)) {
config.acl = acl;
}
if (!_.isEmpty(cdn)) {
config.cdn = cdn;
}
if (!_.isEmpty(region)) {
config.region = region;
}
if (!_.isEmpty(bucketUrl)) {
config.bucketUrl = bucketUrl;
}
try {
Slingshot.createDirective(directiveName, Slingshot.S3Storage, config);
} catch (e) {
console.error('Error configuring S3 ->', e.message);
}
}
const configureSlingshot = _.debounce(() => {
const directives = [
{
name: 'rocketchat-uploads',
key(file, metaContext) {
const path = `${ RocketChat.hostname }/${ metaContext.rid }/${ this.userId }/`;
const upload = {
rid: metaContext.rid,
s3: {
bucket: RocketChat.settings.get('FileUpload_S3_Bucket'),
region: RocketChat.settings.get('FileUpload_S3_Region'),
path
}
};
const fileId = RocketChat.models.Uploads.insertFileInit(this.userId, 's3', file, upload);
return path + fileId;
}
},
{
name: 'rocketchat-avatars',
key(file/*, metaContext*/) {
const path = `${ RocketChat.hostname }/avatars/`;
const user = RocketChat.models.Users.findOneById(this.userId);
const upload = {
username: user && user.username,
s3: {
bucket: RocketChat.settings.get('FileUpload_S3_Bucket'),
region: RocketChat.settings.get('FileUpload_S3_Region'),
path
}
};
RocketChat.models.Uploads.insertFileInitByUsername(user.username, this.userId, 's3', file, upload);
return path + user.username;
}
}
];
const type = RocketChat.settings.get('FileUpload_Storage_Type'); const type = RocketChat.settings.get('FileUpload_Storage_Type');
const bucket = RocketChat.settings.get('FileUpload_S3_Bucket'); const bucket = RocketChat.settings.get('FileUpload_S3_Bucket');
...@@ -54,76 +127,41 @@ const createS3Directive = _.debounce(() => { ...@@ -54,76 +127,41 @@ const createS3Directive = _.debounce(() => {
}); });
if (type === 'AmazonS3' && !_.isEmpty(bucket) && !_.isEmpty(accessKey) && !_.isEmpty(secretKey)) { if (type === 'AmazonS3' && !_.isEmpty(bucket) && !_.isEmpty(accessKey) && !_.isEmpty(secretKey)) {
if (Slingshot._directives[directiveName]) { directives.forEach((conf) => {
delete Slingshot._directives[directiveName]; createDirective(conf.name, { key: conf.key, bucket, accessKey, secretKey, region, acl, cdn, bucketUrl});
} });
const config = { } else {
bucket, directives.forEach((conf) => {
AWSAccessKeyId: accessKey, if (Slingshot._directives[conf.name]) {
AWSSecretAccessKey: secretKey, delete Slingshot._directives[conf.name];
key(file, metaContext) {
const path = `${ RocketChat.hostname }/${ metaContext.rid }/${ this.userId }/`;
const upload = { s3: {
bucket,
region,
path
}};
const fileId = RocketChat.models.Uploads.insertFileInit(metaContext.rid, this.userId, 's3', file, upload);
return path + fileId;
} }
}; });
if (!_.isEmpty(acl)) {
config.acl = acl;
}
if (!_.isEmpty(cdn)) {
config.cdn = cdn;
}
if (!_.isEmpty(region)) {
config.region = region;
}
if (!_.isEmpty(bucketUrl)) {
config.bucketUrl = bucketUrl;
}
try {
Slingshot.createDirective(directiveName, Slingshot.S3Storage, config);
} catch (e) {
SystemLogger.error('Error configuring S3 ->', e.message);
}
} else if (Slingshot._directives[directiveName]) {
delete Slingshot._directives[directiveName];
} }
}, 500); }, 500);
RocketChat.settings.get('FileUpload_Storage_Type', createS3Directive); RocketChat.settings.get('FileUpload_Storage_Type', configureSlingshot);
RocketChat.settings.get('FileUpload_S3_Bucket', createS3Directive); RocketChat.settings.get('FileUpload_S3_Bucket', configureSlingshot);
RocketChat.settings.get('FileUpload_S3_Acl', createS3Directive); RocketChat.settings.get('FileUpload_S3_Acl', configureSlingshot);
RocketChat.settings.get('FileUpload_S3_AWSAccessKeyId', function(key, value) { RocketChat.settings.get('FileUpload_S3_AWSAccessKeyId', function(key, value) {
S3accessKey = value; S3accessKey = value;
createS3Directive(); configureSlingshot();
}); });
RocketChat.settings.get('FileUpload_S3_AWSSecretAccessKey', function(key, value) { RocketChat.settings.get('FileUpload_S3_AWSSecretAccessKey', function(key, value) {
S3secretKey = value; S3secretKey = value;
createS3Directive(); configureSlingshot();
}); });
RocketChat.settings.get('FileUpload_S3_URLExpiryTimeSpan', function(key, value) { RocketChat.settings.get('FileUpload_S3_URLExpiryTimeSpan', function(key, value) {
S3expiryTimeSpan = value; S3expiryTimeSpan = value;
createS3Directive(); configureSlingshot();
}); });
RocketChat.settings.get('FileUpload_S3_CDN', createS3Directive); RocketChat.settings.get('FileUpload_S3_CDN', configureSlingshot);
RocketChat.settings.get('FileUpload_S3_Region', createS3Directive); RocketChat.settings.get('FileUpload_S3_Region', configureSlingshot);
RocketChat.settings.get('FileUpload_S3_BucketURL', createS3Directive); RocketChat.settings.get('FileUpload_S3_BucketURL', configureSlingshot);
...@@ -16,7 +16,7 @@ FileUpload.delete = function(fileId) { ...@@ -16,7 +16,7 @@ FileUpload.delete = function(fileId) {
this.handlers[file.store].delete(file); this.handlers[file.store].delete(file);
return RocketChat.models.Uploads.remove(file._id); return RocketChat.models.Uploads.deleteFile(file._id);
}; };
FileUpload.get = function(file, req, res, next) { FileUpload.get = function(file, req, res, next) {
......
...@@ -29,9 +29,8 @@ RocketChat.models.Uploads = new class extends RocketChat.models._Base ...@@ -29,9 +29,8 @@ RocketChat.models.Uploads = new class extends RocketChat.models._Base
return @find fileQuery, fileOptions return @find fileQuery, fileOptions
insertFileInit: (roomId, userId, store, file, extra) -> insertFileInit: (userId, store, file, extra) ->
fileData = fileData =
rid: roomId
userId: userId userId: userId
store: store store: store
complete: false complete: false
...@@ -49,6 +48,23 @@ RocketChat.models.Uploads = new class extends RocketChat.models._Base ...@@ -49,6 +48,23 @@ RocketChat.models.Uploads = new class extends RocketChat.models._Base
return file return file
insertFileInitByUsername: (username, userId, store, file, extra) ->
fileData =
_id: username
userId: userId
store: store
complete: false
uploading: true
progress: 0
extension: s.strRightBack(file.name, '.')
uploadedAt: new Date()
_.extend(fileData, file, extra);
file = @insertOrUpsert fileData
return file
updateFileComplete: (fileId, userId, file) -> updateFileComplete: (fileId, userId, file) ->
if not fileId if not fileId
return return
...@@ -65,9 +81,40 @@ RocketChat.models.Uploads = new class extends RocketChat.models._Base ...@@ -65,9 +81,40 @@ RocketChat.models.Uploads = new class extends RocketChat.models._Base
update.$set = _.extend file, update.$set update.$set = _.extend file, update.$set
if @model.direct?.insert? if @model.direct?.update?
result = @model.direct.update filter, update
else
result = @update filter, update
return result
updateFileCompleteByUsername: (username, userId, url) ->
if not username
return
filter =
username: username
userId: userId
update =
$set:
complete: true
uploading: false
progress: 1
url: url
if @model.direct?.update?
result = @model.direct.update filter, update result = @model.direct.update filter, update
else else
result = @update filter, update result = @update filter, update
return result return result
findOneByUsername: (username) ->
return @findOne username: username
deleteFile: (fileId) ->
if @model.direct?.remove?
return @model.direct.remove { _id: fileId }
else
return @remove { _id: fileId }
import toastr from 'toastr' import toastr from 'toastr'
import mime from 'mime-type/with-db'
Template.avatarPrompt.onCreated -> Template.avatarPrompt.onCreated ->
self = this self = this
self.suggestions = new ReactiveVar self.suggestions = new ReactiveVar
...@@ -38,7 +40,7 @@ Template.avatarPrompt.helpers ...@@ -38,7 +40,7 @@ Template.avatarPrompt.helpers
return '@'+Meteor.user()?.username return '@'+Meteor.user()?.username
Template.avatarPrompt.events Template.avatarPrompt.events
'click .select-service': -> 'click .select-service': (event, instance) ->
if @service is 'initials' if @service is 'initials'
Meteor.call 'resetAvatar', (err) -> Meteor.call 'resetAvatar', (err) ->
if err?.details?.timeToReset? if err?.details?.timeToReset?
...@@ -60,13 +62,29 @@ Template.avatarPrompt.events ...@@ -60,13 +62,29 @@ Template.avatarPrompt.events
else else
toastr.error t('Please_enter_value_for_url') toastr.error t('Please_enter_value_for_url')
else else
tmpService = @service files = instance.find('input[type=file]').files
Meteor.call 'setAvatarFromService', @blob, @contentType, @service, (err) -> if not files or files.length is 0
if err?.details?.timeToReset? files = e.dataTransfer?.files or []
toastr.error t('error-too-many-requests', { seconds: parseInt(err.details.timeToReset / 1000) })
else for file in files
toastr.success t('Avatar_changed_successfully') Object.defineProperty(file, 'type', { value: mime.lookup(file.name) })
RocketChat.callbacks.run('userAvatarSet', tmpService)
record =
name: files[0].name
size: files[0].size
type: files[0].type
# description: document.getElementById('file-description').value
upload = fileUploadHandler 'rocketchat-avatars', record, files[0]
# upload.onProgress = (progress) ->
# console.log 'progress ->', progress
upload.start (error, result) ->
if result
Meteor.call 'saveAvatarFile', result, () =>
toastr.success t('Avatar_changed_successfully')
RocketChat.callbacks.run('userAvatarSet', @service)
'click .login-with-service': (event, template) -> 'click .login-with-service': (event, template) ->
loginWithService = "loginWith#{_.capitalize(this)}" loginWithService = "loginWith#{_.capitalize(this)}"
......
/* globals fileUploadHandler, Handlebars, fileUpload */
/* exported fileUpload */
function readAsDataURL(file, callback) {
const reader = new FileReader();
reader.onload = ev => callback(ev.target.result, file);
return reader.readAsDataURL(file);
}
function getUploadPreview(file, callback) {
// If greater then 10MB don't try and show a preview
if (file.file.size > (10 * 1000000)) {
return callback(file, null);
} else if ((file.file.type.indexOf('audio') > -1) || (file.file.type.indexOf('video') > -1) || (file.file.type.indexOf('image') > -1)) {
file.type = file.file.type.split('/')[0];
return readAsDataURL(file.file, content => callback(file, content));
} else {
return callback(file, null);
}
}
function formatBytes(bytes, decimals) {
if (bytes === 0) {
return '0 Bytes';
}
const k = 1000;
const dm = (decimals + 1) || 3;
const sizes = [
'Bytes',
'KB',
'MB',
'GB',
'TB',
'PB'
];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${ parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) } ${ sizes[i] }`;
}
fileUpload = function(filesToUpload) {
const roomId = Session.get('openedRoom');
const files = [].concat(filesToUpload);
function consume() {
const file = files.pop();
if ((file == null)) {
swal.close();
return;
}
if (!RocketChat.fileUploadIsValidContentType(file.file.type)) {
swal({
title: t('FileUpload_MediaType_NotAccepted'),
text: file.file.type || `*.${ s.strRightBack(file.file.name, '.') }`,
type: 'error',
timer: 3000
});
return;
}
if (file.file.size === 0) {
swal({
title: t('FileUpload_File_Empty'),
type: 'error',
timer: 1000
});
return;
}
return getUploadPreview(file, function(file, preview) {
let text = '';
if (file.type === 'audio') {
text = `\
<div class='upload-preview'>
<audio style="width: 100%;" controls="controls">
<source src="${ preview }" type="audio/wav">
Your browser does not support the audio element.
</audio>
</div>
<div class='upload-preview-title'>
<input id='file-name' style='display: inherit;' value='${ Handlebars._escape(file.name) }' placeholder='${ t('Upload_file_name') }'>
<input id='file-description' style='display: inherit;' value='' placeholder='${ t('Upload_file_description') }'>
</div>`;
} else if (file.type === 'video') {
text = `\
<div class='upload-preview'>
<video style="width: 100%;" controls="controls">
<source src="${ preview }" type="video/webm">
Your browser does not support the video element.
</video>
</div>
<div class='upload-preview-title'>
<input id='file-name' style='display: inherit;' value='${ Handlebars._escape(file.name) }' placeholder='${ t('Upload_file_name') }'>
<input id='file-description' style='display: inherit;' value='' placeholder='${ t('Upload_file_description') }'>
</div>`;
} else if (file.type === 'image') {
text = `\
<div class='upload-preview'>
<div class='upload-preview-file' style='background-image: url(${ preview })'></div>
</div>
<div class='upload-preview-title'>
<input id='file-name' style='display: inherit;' value='${ Handlebars._escape(file.name) }' placeholder='${ t('Upload_file_name') }'>
<input id='file-description' style='display: inherit;' value='' placeholder='${ t('Upload_file_description') }'>
</div>`;
} else {
const fileSize = formatBytes(file.file.size);
text = `\
<div class='upload-preview'>
<div>${ Handlebars._escape(file.name) } - ${ fileSize }</div>
</div>
<div class='upload-preview-title'>
<input id='file-name' style='display: inherit;' value='${ Handlebars._escape(file.name) }' placeholder='${ t('Upload_file_name') }'>
<input id='file-description' style='display: inherit;' value='' placeholder='${ t('Upload_file_description') }'>
</div>`;
}
return swal({
title: t('Upload_file_question'),
text,
showCancelButton: true,
closeOnConfirm: false,
closeOnCancel: false,
confirmButtonText: t('Send'),
cancelButtonText: t('Cancel'),
html: true
}, function(isConfirm) {
consume();
if (isConfirm !== true) {
return;
}
const record = {
name: document.getElementById('file-name').value || file.name || file.file.name,
size: file.file.size,
type: file.file.type,
rid: roomId,
description: document.getElementById('file-description').value
};
const upload = fileUploadHandler('rocketchat-uploads', record, file.file);
let uploading = Session.get('uploading') || [];
uploading.push({
id: upload.id,
name: upload.getFileName(),
percentage: 0
});
Session.set('uploading', uploading);
upload.onProgress = function(progress) {
uploading = Session.get('uploading');
const item = _.findWhere(uploading, {id: upload.id});
if (item != null) {
item.percentage = Math.round(progress * 100) || 0;
return Session.set('uploading', uploading);
}
};
upload.start(function(error, file) {
if (error) {
let uploading = Session.get('uploading');
if (!Array.isArray(uploading)) {
uploading = [];
}
const item = _.findWhere(uploading, { id: this.id });
if (_.isObject(item)) {
item.error = error.error;
item.percentage = 0;
} else {
uploading.push({
error: error.error,
percentage: 0
});
}
Session.set('uploading', uploading);
return;
} else if (file) {
const uploading = Session.get('uploading');
if (uploading !== null) {
const item = _.findWhere(uploading, {
id: this.id
});
return Session.set('uploading', _.without(uploading, item));
}
}
});
Tracker.autorun(function(c) {
const cancel = Session.get(`uploading-cancel-${ upload.id }`);
if (cancel) {
let item;
upload.stop();
c.stop();
uploading = Session.get('uploading');
if (uploading != null) {
item = _.findWhere(uploading, {id: upload.id});
if (item != null) {
item.percentage = 0;
}
Session.set('uploading', uploading);
}
return Meteor.setTimeout(function() {
uploading = Session.get('uploading');
if (uploading != null) {
item = _.findWhere(uploading, {id: upload.id});
return Session.set('uploading', _.without(uploading, item));
}
}
, 1000);
}
});
});
});
}
consume();
};
...@@ -38,7 +38,7 @@ Package.onUse(function(api) { ...@@ -38,7 +38,7 @@ Package.onUse(function(api) {
api.addFiles('client/lib/chatMessages.coffee', 'client'); api.addFiles('client/lib/chatMessages.coffee', 'client');
api.addFiles('client/lib/collections.js', 'client'); api.addFiles('client/lib/collections.js', 'client');
api.addFiles('client/lib/customEventPolyfill.js', 'client'); api.addFiles('client/lib/customEventPolyfill.js', 'client');
api.addFiles('client/lib/fileUpload.coffee', 'client'); api.addFiles('client/lib/fileUpload.js', 'client');
api.addFiles('client/lib/fireEvent.js', 'client'); api.addFiles('client/lib/fireEvent.js', 'client');
api.addFiles('client/lib/iframeCommands.js', 'client'); api.addFiles('client/lib/iframeCommands.js', 'client');
api.addFiles('client/lib/menu.js', 'client'); api.addFiles('client/lib/menu.js', 'client');
...@@ -108,4 +108,6 @@ Package.onUse(function(api) { ...@@ -108,4 +108,6 @@ Package.onUse(function(api) {
api.addFiles('client/views/app/videoCall/videoButtons.js', 'client'); api.addFiles('client/views/app/videoCall/videoButtons.js', 'client');
api.addFiles('client/views/app/videoCall/videoCall.js', 'client'); api.addFiles('client/views/app/videoCall/videoCall.js', 'client');
api.addFiles('client/views/app/photoswipe.js', 'client'); api.addFiles('client/views/app/photoswipe.js', 'client');
api.export('fileUpload');
}); });
...@@ -19,6 +19,13 @@ Meteor.methods({ ...@@ -19,6 +19,13 @@ Meteor.methods({
const user = Meteor.user(); const user = Meteor.user();
return RocketChat.setUserAvatar(user, dataURI, contentType, service); return RocketChat.setUserAvatar(user, dataURI, contentType, service);
},
saveAvatarFile(file) {
const user = RocketChat.models.Users.findOneById(Meteor.userId());
// console.log('user ->', user);
// console.log('file ->', file);
RocketChat.models.Uploads.updateFileCompleteByUsername(user.username, Meteor.userId(), file.url);
} }
}); });
......
...@@ -35,13 +35,11 @@ Meteor.startup(function() { ...@@ -35,13 +35,11 @@ Meteor.startup(function() {
transformWrite transformWrite
}); });
return WebApp.connectHandlers.use('/avatar/', Meteor.bindEnvironment(function(req, res/*, next*/) { WebApp.connectHandlers.use('/avatar/', Meteor.bindEnvironment(function(req, res/*, next*/) {
const params = { const params = {
username: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, '')) username: decodeURIComponent(req.url.replace(/^\//, '').replace(/\?.*$/, ''))
}; };
let username = params.username.replace(/\.jpg$/, '').replace(/^@/, '');
if (_.isEmpty(params.username)) { if (_.isEmpty(params.username)) {
res.writeHead(403); res.writeHead(403);
res.write('Forbidden'); res.write('Forbidden');
...@@ -49,24 +47,23 @@ Meteor.startup(function() { ...@@ -49,24 +47,23 @@ Meteor.startup(function() {
return; return;
} }
let file; return getNewAvatar(params, req, res);
}));
});
if (params.username[0] !== '@') {
if (Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm) {
const user = RocketChat.models.Users.findOneByUsername(username);
if (user && user.services && user.services.sandstorm && user.services.sandstorm.picture) {
res.setHeader('Location', user.services.sandstorm.picture);
res.writeHead(302);
res.end();
return;
}
}
file = RocketChatFileAvatarInstance.getFileWithReadStream(encodeURIComponent(`${ username }.jpg`)); function getNewAvatar(params, req, res) {
} const match = /^\/([^?]*)/.exec(req.url);
if (match[1]) {
let username = decodeURIComponent(match[1]);
const file = RocketChat.models.Uploads.findOneByUsername(username);
if (file) {
res.setHeader('Content-Security-Policy', 'default-src \'none\'');
res.setHeader('Content-Disposition', 'inline'); return FileUpload.get(file, req, res);
if (!file) { } else {
res.setHeader('Content-Type', 'image/svg+xml'); res.setHeader('Content-Type', 'image/svg+xml');
res.setHeader('Cache-Control', 'public, max-age=0'); res.setHeader('Cache-Control', 'public, max-age=0');
res.setHeader('Expires', '-1'); res.setHeader('Expires', '-1');
...@@ -120,35 +117,113 @@ Meteor.startup(function() { ...@@ -120,35 +117,113 @@ Meteor.startup(function() {
return; return;
} }
}
res.writeHead(404);
res.end();
return;
}
function getOldAvatar(params, req, res) {
let username = params.username.replace(/\.jpg$/, '').replace(/^@/, '');
let file;
if (params.username[0] !== '@') {
if (Meteor.settings && Meteor.settings.public && Meteor.settings.public.sandstorm) {
const user = RocketChat.models.Users.findOneByUsername(username);
if (user && user.services && user.services.sandstorm && user.services.sandstorm.picture) {
res.setHeader('Location', user.services.sandstorm.picture);
res.writeHead(302);
res.end();
return;
}
}
file = RocketChatFileAvatarInstance.getFileWithReadStream(encodeURIComponent(`${ username }.jpg`));
}
res.setHeader('Content-Disposition', 'inline');
if (!file) {
res.setHeader('Content-Type', 'image/svg+xml');
res.setHeader('Cache-Control', 'public, max-age=0');
res.setHeader('Expires', '-1');
res.setHeader('Last-Modified', 'Thu, 01 Jan 2015 00:00:00 GMT');
const reqModifiedHeader = req.headers['if-modified-since']; const reqModifiedHeader = req.headers['if-modified-since'];
if (reqModifiedHeader) { if (reqModifiedHeader) {
if (reqModifiedHeader === (file.uploadDate && file.uploadDate.toUTCString())) { if (reqModifiedHeader === 'Thu, 01 Jan 2015 00:00:00 GMT') {
res.setHeader('Last-Modified', reqModifiedHeader);
res.writeHead(304); res.writeHead(304);
res.end(); res.end();
return; return;
} }
} }
res.setHeader('Cache-Control', 'public, max-age=0'); const colors = ['#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B'];
res.setHeader('Expires', '-1');
res.setHeader('Last-Modified', (file.uploadDate && file.uploadDate.toUTCString()) || new Date().toUTCString());
res.setHeader('Content-Length', file.length);
if (file.contentType) { if (RocketChat.settings.get('UI_Use_Name_Avatar')) {
res.setHeader('Content-Type', file.contentType); const user = RocketChat.models.Users.findOneByUsername(username, {
} else { fields: {
file.readStream.once('data', function(chunk) { name: 1
const fileType = getFileType(chunk);
if (fileType) {
return res.setHeader('Content-Type', fileType.mime);
} else {
return res.setHeader('Content-Type', 'image/jpeg');
} }
}); });
if (user && user.name) {
username = user.name;
}
} }
file.readStream.pipe(res); let color = '';
})); let initials = '';
});
if (username === '?') {
color = '#000';
initials = username;
} else {
const position = username.length % colors.length;
color = colors[position];
username = username.replace(/[^A-Za-z0-9]/g, '.').replace(/\.+/g, '.').replace(/(^\.)|(\.$)/g, '');
const usernameParts = username.split('.');
initials = usernameParts.length > 1 ? _.first(usernameParts)[0] + _.last(usernameParts)[0] : username.replace(/[^A-Za-z0-9]/g, '').substr(0, 2);
initials = initials.toUpperCase();
}
const svg = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg xmlns=\"http://www.w3.org/2000/svg\" pointer-events=\"none\" width=\"50\" height=\"50\" style=\"width: 50px; height: 50px; background-color: ${ color };\">\n <text text-anchor=\"middle\" y=\"50%\" x=\"50%\" dy=\"0.36em\" pointer-events=\"auto\" fill=\"#ffffff\" font-family=\"Helvetica, Arial, Lucida Grande, sans-serif\" style=\"font-weight: 400; font-size: 28px;\">\n ${ initials }\n </text>\n</svg>`;
res.write(svg);
res.end();
return;
}
const reqModifiedHeader = req.headers['if-modified-since'];
if (reqModifiedHeader) {
if (reqModifiedHeader === (file.uploadDate && file.uploadDate.toUTCString())) {
res.setHeader('Last-Modified', reqModifiedHeader);
res.writeHead(304);
res.end();
return;
}
}
res.setHeader('Cache-Control', 'public, max-age=0');
res.setHeader('Expires', '-1');
res.setHeader('Last-Modified', (file.uploadDate && file.uploadDate.toUTCString()) || new Date().toUTCString());
res.setHeader('Content-Length', file.length);
if (file.contentType) {
res.setHeader('Content-Type', file.contentType);
} else {
file.readStream.once('data', function(chunk) {
const fileType = getFileType(chunk);
if (fileType) {
return res.setHeader('Content-Type', fileType.mime);
} else {
return res.setHeader('Content-Type', 'image/jpeg');
}
});
}
file.readStream.pipe(res);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment