// doc: https://www.dropbox.com/developers/documentation/http/documentation var http = require('request-promise'), http_stream = require('request'), Path = require('path'), config = require('../../../config_server'), toString = require('stream-to-string'), Readable = require('stream').Readable; function query(params, uri, method = 'GET', data, opts = {}){ if(!opts.headers) opts.headers = {}; opts.headers['Authorization'] = 'Bearer '+params.bearer; opts.uri = uri; opts.method = method; if(data && typeof data === 'object'){ opts.body = JSON.stringify(data); opts.headers["Content-Type"] = "application/json"; } return http(opts) .then((res) => Promise.resolve(JSON.parse(res))) .catch((res) => { if(res && res.response && res.response.body){ return Promise.reject(res.response.body); }else{ return Promise.reject(res); } }) } function query_stream(params, uri, method = 'GET', data, opts = {}){ if(!opts.headers) opts.headers = {}; opts.headers['Authorization'] = 'Bearer '+params.bearer; opts.uri = uri; opts.method = method; opts.body = data; return Promise.resolve(http_stream(opts)); } module.exports = { auth: function(params){ let url = "https://www.dropbox.com/oauth2/authorize?client_id="+config.dropbox.clientID+"&response_type=token&redirect_uri="+config.dropbox.redirectURI+"&state=dropbox" return Promise.resolve(url) }, test: function(params){ return query(params, "https://api.dropboxapi.com/2/users/get_current_account", "POST") .then((opts) => Promise.resolve(params)) .catch((err) => Promise.reject({message: 'Dropbox didn\'t gave us access to your account', code: "NOT_AUTHENTICATED"})) }, cat: function(path, params){ return query_stream(params, "https://content.dropboxapi.com/2/files/download", "POST", null, { headers: { "Dropbox-API-Arg": JSON.stringify({path: path}) } }).then((res) => { // dropbox send silly mimetype like 'application/octet-stream' for pdf files ... // We can't trust them on this, so we get rid of it. In our case, it will be set by the file controller const newRes = res.on('response', function(res) { delete res.headers['content-type']; }); return Promise.resolve(newRes); }) }, ls: function(path, params){ if(path === '/') path = ''; return query(params, "https://api.dropboxapi.com/2/files/list_folder", "POST", {path: path, recursive: false, include_deleted: false, include_media_info: true}) .then((res) => { let files = res.entries.map((file) => { let tmp = { size: file.size, time: new Date(file.client_modified).getTime(), type: file['.tag'] === 'file' ? 'file' : 'directory', name: file.name }; return tmp; }); return Promise.resolve(files); }); }, write: function(path, content, params){ return write_file(path, content, params); }, rm: function(path, params){ return query(params, "https://api.dropboxapi.com/2/files/delete_v2", "POST", {path: path}) .then((res) => Promise.resolve('ok')); }, mv: function(from, to, params){ return query(params, "https://api.dropboxapi.com/2/files/move_v2", "POST", {from_path: from, to_path: to}) .then((res) => verifyDropbox(res, to, params, 10)) .catch(err => Promise.reject({message: JSON.parse(err).error, code: "DROPBOX_MOVE"})); }, mkdir: function(path, params){ path = path.replace(/\/$/, ''); return query(params, "https://api.dropboxapi.com/2/files/create_folder_v2", "POST", {path: path, autorename: false}) .then((res) => verifyDropbox(res, path, params, 10)) .then((res) => Promise.resolve('ok')); }, touch: function(path, params){ var stream = new Readable(); stream.push(''); stream.push(null); return write_file(path, stream, params); } } function write_file(path, content, params){ return process(path, content, params) .then((res) => retryOnError(res, path, content, params, 5)) .then((res) => verifyDropbox(res, path, params, 10)) function process(path, content, params){ return query_stream(params, "https://content.dropboxapi.com/2/files/upload", "POST", content, { headers: { "Dropbox-API-Arg": JSON.stringify({ path: path, autorename: false, mode: "overwrite" }), "Content-Type": "application/octet-stream" } }).then(toString) } function retryOnError(body, path, content, params, n = 5){ body = JSON.parse(body); if(body && body.error){ return sleep(Math.abs(5 - n) * 1000) .then(() => process(path, content, params, n -1)) }else{ return Promise.resolve(body); } } } function verifyDropbox(keep, path, params, n = 10){ let folder_path = Path.posix.dirname(path).replace(/\/$/, ''); if(folder_path === '.'){ folder_path = ''; } return sleep(Math.abs(10 - n) * 300) .then(() => query(params, "https://api.dropboxapi.com/2/files/list_folder", "POST", {path: folder_path, recursive: false, include_deleted: false, include_media_info: true})) .then((res) => { let found = res.entries.find((function(file){ return file.path_display === path? true : false })); if(found){ return Promise.resolve(keep) }else{ if(n > 0){ return verifyDropbox(keep, path, params, n - 1) }else{ return Promise.reject({message: 'dropbox didn\' create the file or was taking too long to do so', code: 'DROPBOX_WRITE_ERROR'}) } } }) } function sleep(t=1000, arg){ return new Promise((done) => { setTimeout(function(){ done(arg); }, t) }) }