diff --git a/client/helpers/ajax.js b/client/helpers/ajax.js index c0b59ca5..7c180d13 100644 --- a/client/helpers/ajax.js +++ b/client/helpers/ajax.js @@ -98,19 +98,22 @@ function handle_error_response(xhr, err){ return message; })(xhr.responseText); - if(xhr.status === 500){ - err({message: message || "Oups something went wrong with our servers"}) + if(navigator.onLine === false){ + err({message: 'Connection Lost', code: "NO_INTERNET"}); + }else if(xhr.status === 500){ + err({message: message || "Oups something went wrong with our servers", code: "INTERNAL_SERVER_ERROR"}); }else if(xhr.status === 401){ if(location.pathname !== '/login'){ location.pathname = "/login"; } - err({message: message || "Authentication error"}); + err({message: message || "Authentication error", code: "Unauthorized"}); }else if(xhr.status === 403){ - err({message: message || "You can\'t do that"}); + err({message: message || "You can\'t do that", code: "Forbidden"}); }else if(xhr.status === 413){ - err({message: message || "Payload too large"}); - }else if(navigator.onLine === false){ - err({status: xhr.status, code: "CONNECTION_LOST", message: 'Connection Lost'}); + err({message: message || "Payload too large", code: "PAYLOAD_TOO_LARGE"}); + }else if(xhr.status === 502){ + err({message: message || "The destination is acting weird", code: "BAD_GATEWAY"}); + }else if(xhr.status === 409){ + err({message: message || "Oups you just ran into a conflict", code: "CONFLICT"}); }else{ - err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'}); + err({message: message || 'Oups something went wrong'}); } - } diff --git a/client/model/files.js b/client/model/files.js index 113ceeb5..290f603b 100644 --- a/client/model/files.js +++ b/client/model/files.js @@ -46,8 +46,7 @@ class FileSystem{ return cache.get(cache.FILE_PATH, path, false).then((_files) => { if(_files && _files.results){ let _files_virtual_to_keep = _files.results.filter((file) => { - return file.icon === 'loading' && - (new Date()).getTime() - file._timestamp < 1000*60*60; + return file.icon === 'loading'; }); // update file results for(let i=0; i<_files_virtual_to_keep.length; i++){ @@ -95,6 +94,10 @@ class FileSystem{ }else{ return this._replace(path, 'error'); } + }) + .catch((err) => { + this._replace(path, 'error'); + return Promise.reject(err); }); } @@ -135,7 +138,11 @@ class FileSystem{ return this._replace(path, 'loading') .then(() => cache.put(cache.FILE_CONTENT, path, file)) .then(() => http_post(url, formData, 'multipart')) - .then((res)=> res.status === 'ok'? this._replace(path) : this._replace(path, 'error')); + .then((res)=> res.status === 'ok'? this._replace(path) : this._replace(path, 'error')) + .catch((err) => { + this._replace(path, 'error'); + return Promise.reject(err); + }); } mkdir(path){ @@ -143,7 +150,11 @@ class FileSystem{ return this._add(path, 'loading') .then(() => this._add(path, 'loading')) .then(() => http_get(url)) - .then((res) => res.status === 'ok'? this._replace(path) : this._replace(path, 'error')); + .then((res) => res.status === 'ok'? this._replace(path) : this._replace(path, 'error')) + .catch((err) => { + this._replace(path, 'error'); + return Promise.reject(err); + }); } touch(path, file){ @@ -159,7 +170,11 @@ class FileSystem{ return http_get(url); } }) - .then((res) => res.status === 'ok'? this._replace(path) : this._replace(path, 'error')); + .then((res) => res.status === 'ok'? this._replace(path) : this._replace(path, 'error')) + .catch((err) => { + this._replace(path, 'error'); + return Promise.reject(err); + }); } mv(from, to){ @@ -168,18 +183,23 @@ class FileSystem{ return ui_before_request(from, to) .then(() => this._ls_from_cache(dirname(from))) .then(() => this._ls_from_cache(dirname(to))) - .then(() => http_get(url) - .then((res) => { - if(res.status === 'ok'){ - return ui_when_success.call(this, from, to) - .then(() => this._ls_from_cache(dirname(from))) - .then(() => this._ls_from_cache(dirname(to))); - }else{ - return ui_when_fail.call(this, from, to) - .then(() => this._ls_from_cache(dirname(from))) - .then(() => this._ls_from_cache(dirname(to))); - } - })); + .then(() => http_get(url).then((res) => { + if(res.status === 'ok'){ + return ui_when_success.call(this, from, to) + .then(() => this._ls_from_cache(dirname(from))) + .then(() => this._ls_from_cache(dirname(to))); + }else{ + return ui_when_fail.call(this, from, to) + .then(() => this._ls_from_cache(dirname(from))) + .then(() => this._ls_from_cache(dirname(to))); + } + })) + .catch((err) => { + ui_when_fail.call(this, from, to) + .then(() => this._ls_from_cache(dirname(from))) + .then(() => this._ls_from_cache(dirname(to))); + return Promise.reject(err); + }); function ui_before_request(from, to){ return update_from() @@ -201,7 +221,6 @@ class FileSystem{ if(file.name === basename(from)){ file.name = basename(to); file.icon = 'loading'; - file._timestamp = (new Date()).getTime(); _file = file; } return file; @@ -232,7 +251,10 @@ class FileSystem{ .then((res_from) => { if(!res_from || !res_from.results) return Promise.reject(); res_from.results = res_from.results.map((file) => { - if(file.name === basename(from)){ + if( + (dirname(from) === dirname(to) && file.name === basename(to)) || + (dirname(from) !== dirname(to) && file.name === basename(from)) + ){ file.icon = 'error'; } return file; @@ -339,11 +361,9 @@ class FileSystem{ if(file.name === basename(path)){ if(!icon){ delete file.icon; - delete file._timestamp; } if(icon){ file.icon = icon; - file._timestamp = (new Date()).getTime(); } } return file; @@ -360,8 +380,7 @@ class FileSystem{ if(!res) return Promise.resolve(); let file = { name: basename(path), - type: /\/$/.test(path) ? 'directory' : 'file', - _timestamp: (new Date()).getTime() + type: /\/$/.test(path) ? 'directory' : 'file' }; if(icon) file.icon = icon; res.results.push(file); diff --git a/server/ctrl/files.js b/server/ctrl/files.js index 0286e6d4..42a85e59 100644 --- a/server/ctrl/files.js +++ b/server/ctrl/files.js @@ -18,16 +18,12 @@ app.use(function(req, res, next){ // list files app.get('/ls', function(req, res){ - let path = pathBuilder(req); + const path = pathBuilder(req); if(path){ Files .ls(path, req.cookies.auth) - .then(function(results){ - res.send({status: 'ok', results: results}); - }) - .catch(function(err){ - res.send({status: 'error', message: err.message || 'cannot fetch files', trace: err}); - }); + .then(function(results){ res.send({status: 'ok', results: results}); }) + .catch(function(err){ errorHandler(res, err, 'cannot fetch files'); }); }else{ res.send({status: 'error', message: 'unknown path'}); } @@ -35,7 +31,7 @@ app.get('/ls', function(req, res){ // get a file content app.get('/cat', function(req, res){ - let path = pathBuilder(req); + const path = pathBuilder(req); res.clearCookie("download"); if(path){ Files.cat(path, req.cookies.auth, res) @@ -43,9 +39,7 @@ app.get('/cat', function(req, res){ res.set('Content-Type', mime.getMimeType(path)); stream.pipe(res); }) - .catch(function(err){ - res.send({status: 'error', message: err.message || 'couldn\t read the file', trace: err}); - }); + .catch(function(err){ errorHandler(res, err, 'couldn\'t read the file'); }); }else{ res.send({status: 'error', message: 'unknown path'}); } @@ -54,22 +48,20 @@ app.get('/cat', function(req, res){ // create/update a file // https://github.com/pillarjs/multiparty app.post('/cat', function(req, res){ - var form = new multiparty.Form(), - path = pathBuilder(req); + const form = new multiparty.Form(), + path = pathBuilder(req); if(path){ form.on('part', function(part) { part.on('error', function(err){ - res.send({status: 'error', message: 'internal error'}); + errorHandler(res, {code: "INTERNAL_ERROR", message: "internal error"}, 'internal error'); }); Files.write(path, part, req.cookies.auth) .then(function(result){ res.send({status: 'ok'}); }) - .catch(function(err){ - res.send({status: 'error', message: err.message || 'couldn\'t write the file', code: err.code}); - }); + .catch(function(err){ errorHandler(res, err, 'couldn\'t write the file'); }); }); form.parse(req); }else{ @@ -79,19 +71,17 @@ app.post('/cat', function(req, res){ // rename a file/directory app.get('/mv', function(req, res){ - let from = decodeURIComponent(req.query.from), - to = decodeURIComponent(req.query.to); + req.query.path = req.query.from; + const from = pathBuilder(req); + req.query.path = req.query.to; + const to = pathBuilder(req) if(from === to){ res.send({status: 'ok'}); }else if(from && to){ Files.mv(from, to, req.cookies.auth) - .then((message) => { - res.send({status: 'ok'}); - }) - .catch((err) => { - res.send({status: 'error', message: err.message || 'couldn\'t rename your file', trace: err}); - }); + .then(function(message){ res.send({status: 'ok'}); }) + .catch(function(err){ errorHandler(res, err, 'couldn\'t rename your file'); }); }else{ res.send({status: 'error', message: 'unknown path'}); } @@ -99,15 +89,11 @@ app.get('/mv', function(req, res){ // delete a file/directory app.get('/rm', function(req, res){ - let path = pathBuilder(req); + const path = pathBuilder(req); if(path){ Files.rm(path, req.cookies.auth) - .then((message) => { - res.send({status: 'ok'}); - }) - .catch((err) => { - res.send({status: 'error', message: err.message || 'couldn\'t delete your file', trace: err}); - }); + .then(function(message){ res.send({status: 'ok'}); }) + .catch(function(err){ errorHandler(res, err, 'couldn\'t delete your file'); }); }else{ res.send({status: 'error', message: 'unknown path'}); } @@ -115,30 +101,22 @@ app.get('/rm', function(req, res){ // create a directory app.get('/mkdir', function(req, res){ - let path = pathBuilder(req); + const path = pathBuilder(req); if(path){ Files.mkdir(path, req.cookies.auth) - .then((message) => { - res.send({status: 'ok'}); - }) - .catch((err) => { - res.send({status: 'error', message: err.message || 'couldn\'t create a directory', trace: err}); - }); + .then(function(message){ res.send({status: 'ok'}); }) + .catch(function(err){ errorHandler(res, err, 'couldn\'t create the directory'); }); }else{ res.send({status: 'error', message: 'unknown path'}); } }); app.get('/touch', function(req, res){ - let path = pathBuilder(req); + const path = pathBuilder(req); if(path){ Files.touch(path, req.cookies.auth) - .then((message) => { - res.send({status: 'ok'}); - }) - .catch((err) => { - res.send({status: 'error', message: err.message || 'couldn\'t create a file', trace: err}); - }); + .then(function(message){ res.send({status: 'ok'}); }) + .catch(function(err){ errorHandler(res, err, 'couldn\'t create the file'); }); }else{ res.send({status: 'error', message: 'unknown path'}); } @@ -150,3 +128,39 @@ module.exports = app; function pathBuilder(req){ return path.join(req.cookies.auth.payload.path || '', decodeURIComponent(req.query.path) || ''); } + +function errorHandler(res, err, defaultMessage){ + const code = { + "INTERNAL_ERROR": {message: "Oops, it seems we had a problem", status: 500}, + "ECONNREFUSED": {message: "Oops, the service you are connected on is not available", status: 502} + }; + const status = function(_code, _status){ + if(code[_code]){ + return code[_code]['status']; + } + _status = parseInt(_status); + if(_status >= 400 && _status < 600){ + return _status; + } + return 404; + }(err.code || err.errno, err.status); + + if(code[err.code || err.errno]){ + res.status(status).send({ + status: 'error', + message: code[err.code]['message'] + }); + }else if(err.message){ + res.status(status).send({ + status: 'error', + message: err.message || 'cannot fetch files', + trace: err + }); + }else{ + res.status(status).send({ + status: 'error', + message: defaultMessage, + trace: err + }); + } +} diff --git a/server/model/files.js b/server/model/files.js index 3c711e75..62858172 100644 --- a/server/model/files.js +++ b/server/model/files.js @@ -7,7 +7,7 @@ var backend = { s3: require('./backend/s3'), git: require('./backend/git') }; - + exports.cat = function(path, params, res){ try{ if(backend[params.type] && typeof backend[params.type].cat === 'function'){ @@ -22,7 +22,7 @@ exports.cat = function(path, params, res){ exports.write = function(path, content, params){ try{ - if(backend[params.type] && typeof backend[params.type].write === 'function'){ + if(backend[params.type] && typeof backend[params.type].write === 'function'){ return backend[params.type].write(path, content, params.payload); }else{ return error('not implemented'); @@ -46,7 +46,7 @@ exports.ls = function(path, params){ exports.mv = function(from, to, params){ try{ - if(backend[params.type] && typeof backend[params.type].mv === 'function'){ + if(backend[params.type] && typeof backend[params.type].mv === 'function'){ return backend[params.type].mv(from, to, params.payload); }else{ return error('not implemented');