Skip to content

Commit

Permalink
[NEW] WebDAV(Nextcloud/ownCloud) Storage Server Option (#11027)
Browse files Browse the repository at this point in the history
Closes #9562,
Part of #7791

Implementation of ownCloud/Nextcloud as file upload store feature. Since it is using generic webdav client, the store compatible with all WebDAV servers (NextCloud, ownCloud and so on). 
![image](https://user-images.githubusercontent.com/14157973/41051962-3a63ef5e-69c0-11e8-9a8a-76ed36308dfb.png)
  • Loading branch information
karakayasemi authored and ggazzo committed Jun 12, 2018
1 parent 2c28b81 commit 2bcca4f
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
"ua-parser-js": "^0.7.18",
"underscore": "^1.9.1",
"underscore.string": "^3.3.4",
"webdav": "^1.5.2",
"wolfy87-eventemitter": "^5.2.4",
"xml-crypto": "^0.10.1",
"xml2js": "^0.4.19",
Expand Down
62 changes: 62 additions & 0 deletions packages/rocketchat-file-upload/server/config/Webdav.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* globals FileUpload */

import _ from 'underscore';
import { FileUploadClass } from '../lib/FileUpload';
import '../../ufs/Webdav/server.js';

const get = function(file, req, res) {
this.store.getReadStream(file._id, file).pipe(res);
};

const copy = function(file, out) {
this.store.getReadStream(file._id, file).pipe(out);
};

const WebdavUploads = new FileUploadClass({
name: 'Webdav:Uploads',
get,
copy
// store setted bellow
});

const WebdavAvatars = new FileUploadClass({
name: 'Webdav:Avatars',
get,
copy
// store setted bellow
});

const WebdavUserDataFiles = new FileUploadClass({
name: 'Webdav:UserDataFiles',
get,
copy
// store setted bellow
});

const configure = _.debounce(function() {
const uploadFolderPath = RocketChat.settings.get('FileUpload_Webdav_Upload_Folder_Path');
const server = RocketChat.settings.get('FileUpload_Webdav_Server_URL');
const username = RocketChat.settings.get('FileUpload_Webdav_Username');
const password = RocketChat.settings.get('FileUpload_Webdav_Password');

if (!server || !username || !password) {
return;
}

const config = {
connection: {
credentials: {
server,
username,
password
}
},
uploadFolderPath
};

WebdavUploads.store = FileUpload.configureUploadsStore('Webdav', WebdavUploads.name, config);
WebdavAvatars.store = FileUpload.configureUploadsStore('Webdav', WebdavAvatars.name, config);
WebdavUserDataFiles.store = FileUpload.configureUploadsStore('Webdav', WebdavUserDataFiles.name, config);
}, 500);

RocketChat.settings.get(/^FileUpload_Webdav_/, configure);
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import './AmazonS3.js';
import './FileSystem.js';
import './GoogleStorage.js';
import './GridFS.js';
import './Webdav.js';
import './Slingshot_DEPRECATED.js';

const configStore = _.debounce(() => {
Expand Down
49 changes: 49 additions & 0 deletions packages/rocketchat-file-upload/server/startup/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ RocketChat.settings.addGroup('FileUpload', function() {
}, {
key: 'GoogleCloudStorage',
i18nLabel: 'GoogleCloudStorage'
}, {
key: 'Webdav',
i18nLabel: 'WebDAV'
}, {
key: 'FileSystem',
i18nLabel: 'FileSystem'
Expand Down Expand Up @@ -180,6 +183,52 @@ RocketChat.settings.addGroup('FileUpload', function() {
});
});

this.section('WebDAV', function() {
this.add('FileUpload_Webdav_Upload_Folder_Path', '', {
type: 'string',
enableQuery: {
_id: 'FileUpload_Storage_Type',
value: 'Webdav'
}
});
this.add('FileUpload_Webdav_Server_URL', '', {
type: 'string',
enableQuery: {
_id: 'FileUpload_Storage_Type',
value: 'Webdav'
}
});
this.add('FileUpload_Webdav_Username', '', {
type: 'string',
enableQuery: {
_id: 'FileUpload_Storage_Type',
value: 'Webdav'
}
});
this.add('FileUpload_Webdav_Password', '', {
type: 'password',
private: true,
enableQuery: {
_id: 'FileUpload_Storage_Type',
value: 'Webdav'
}
});
this.add('FileUpload_Webdav_Proxy_Avatars', false, {
type: 'boolean',
enableQuery: {
_id: 'FileUpload_Storage_Type',
value: 'Webdav'
}
});
this.add('FileUpload_Webdav_Proxy_Uploads', false, {
type: 'boolean',
enableQuery: {
_id: 'FileUpload_Storage_Type',
value: 'Webdav'
}
});
});

this.add('FileUpload_Enabled_Direct', true, {
type: 'boolean',
public: true
Expand Down
6 changes: 6 additions & 0 deletions packages/rocketchat-file-upload/ufs/Webdav/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {UploadFS} from 'meteor/jalik:ufs';

export class WebdavStore extends UploadFS.Store {}

// Add store to UFS namespace
UploadFS.store.Webdav = WebdavStore;
135 changes: 135 additions & 0 deletions packages/rocketchat-file-upload/ufs/Webdav/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import {UploadFS} from 'meteor/jalik:ufs';
import Webdav from 'webdav';
import stream from 'stream';
/**
* WebDAV store
* @param options
* @constructor
*/
export class WebdavStore extends UploadFS.Store {

constructor(options) {

super(options);


const client = new Webdav(
options.connection.credentials.server,
options.connection.credentials.username,
options.connection.credentials.password,
);

options.getPath = function(file) {
if (options.uploadFolderPath[options.uploadFolderPath.length-1] !== '/') {
options.uploadFolderPath += '/';
}
return options.uploadFolderPath + file._id;
};

client.stat(options.uploadFolderPath).catch(function(err) {
if (err.status === '404') {
client.createDirectory(options.uploadFolderPath);
}
});

/**
* Returns the file path
* @param file
* @return {string}
*/
this.getPath = function(file) {
if (file.Webdav) {
return file.Webdav.path;
}
};

/**
* Creates the file in the col lection
* @param file
* @param callback
* @return {string}
*/
this.create = function(file, callback) {
check(file, Object);

if (file._id == null) {
file._id = Random.id();
}

file.Webdav = {
path: options.getPath(file)
};

file.store = this.options.name;
return this.getCollection().insert(file, callback);
};

/**
* Removes the file
* @param fileId
* @param callback
*/
this.delete = function(fileId, callback) {
const file = this.getCollection().findOne({_id: fileId});
client.deleteFile(this.getPath(file), (err, data) => {
if (err) {
console.error(err);
}

callback && callback(err, data);
});
};

/**
* Returns the file read stream
* @param fileId
* @param file
* @param options
* @return {*}
*/
this.getReadStream = function(fileId, file, options = {}) {
const range = {};

if (options.start != null) {
range.start = options.start;
}

if (options.end != null) {
range.end = options.end;
}
return client.createReadStream(this.getPath(file), options);
};

/**
* Returns the file write stream
* @param fileId
* @param file
* @return {*}
*/
this.getWriteStream = function(fileId, file) {
const writeStream = new stream.PassThrough();
const webdavStream = client.createWriteStream(this.getPath(file));

//TODO remove timeout when UploadFS bug resolved
const newListenerCallback = (event, listener) => {
if (event === 'finish') {
process.nextTick(() => {
writeStream.removeListener(event, listener);
writeStream.removeListener('newListener', newListenerCallback);
writeStream.on(event, function() {
setTimeout(listener, 500);
});
});
}
};
writeStream.on('newListener', newListenerCallback);

writeStream.pipe(webdavStream);
return writeStream;
};

}
}

// Add store to UFS namespace
UploadFS.store.Webdav = WebdavStore;
9 changes: 9 additions & 0 deletions packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,15 @@
"FileUpload_S3_URLExpiryTimeSpan": "URLs Expiration Timespan",
"FileUpload_S3_URLExpiryTimeSpan_Description": "Time after which Amazon S3 generated URLs will no longer be valid (in seconds). If set to less than 5 seconds, this field will be ignored.",
"FileUpload_Storage_Type": "Storage Type",
"FileUpload_Webdav_Upload_Folder_Path": "Upload Folder Path",
"FileUpload_Webdav_Upload_Folder_Path_Description": "WebDAV folder path which the files should be uploaded to",
"FileUpload_Webdav_Server_URL": "WebDAV Server Access URL",
"FileUpload_Webdav_Username": "WebDAV Username",
"FileUpload_Webdav_Password": "WebDAV Password",
"FileUpload_Webdav_Proxy_Avatars": "Proxy Avatars",
"FileUpload_Webdav_Proxy_Avatars_Description": "Proxy avatar file transmissions through your server instead of direct access to the asset's URL",
"FileUpload_Webdav_Proxy_Uploads": "Proxy Uploads",
"FileUpload_Webdav_Proxy_Uploads_Description": "Proxy upload file transmissions through your server instead of direct access to the asset's URL",
"Financial_Services": "Financial Services",
"First_Channel_After_Login": "First Channel After Login",
"Flags": "Flags",
Expand Down

0 comments on commit 2bcca4f

Please sign in to comment.