bugfix (app): fix server side issues and proper error handling

This commit is contained in:
Mickael KERJEAN
2018-04-11 23:55:39 +10:00
parent 3e2714fb33
commit d67c200af7
11 changed files with 68 additions and 43 deletions

View File

@ -13,6 +13,8 @@
--super-light: #f4f4f4; --super-light: #f4f4f4;
--error: #f26d6d; --error: #f26d6d;
--success: #63d9b1; --success: #63d9b1;
--dark: #313538;
} }
html { html {

View File

@ -115,9 +115,15 @@ export class PathElementWrapper extends React.Component {
super(props); super(props);
} }
limitSize(str){ limitSize(str, is_highlight = false){
if(str.length > 27){ if(is_highlight === true){
return str.substring(0,20)+'...'; if(str.length > 30){
return str.substring(0,12).trim()+'...'+str.substring(str.length - 10, str.length).trim();
}
}else{
if(str.length > 27){
return str.substring(0,20).trim()+'...';
}
} }
return str; return str;
} }
@ -135,7 +141,7 @@ export class PathElementWrapper extends React.Component {
<NgIf cond={this.props.path.minify === true}> <NgIf cond={this.props.path.minify === true}>
... ...
<span className="title"> <span className="title">
{this.limitSize(this.props.path.label)} {this.limitSize(this.props.path.label, true)}
</span> </span>
</NgIf> </NgIf>
</Link> </Link>

View File

@ -39,10 +39,12 @@
display: inline-block; display: inline-block;
.label{color: var(--color);padding: 2px 5px;} .label{color: var(--color);padding: 2px 5px;}
a.label{ a.label{
position: relative;
color: var(--light); color: var(--light);
span.title{ span.title{
position: absolute; position: absolute;
background: var(--color); left: 0;
background: var(--dark);
color: white; color: white;
font-size: 0.8em; font-size: 0.8em;
opacity: 0; opacity: 0;
@ -50,7 +52,7 @@
border-radius: 2px; border-radius: 2px;
white-space: nowrap; white-space: nowrap;
padding: 3px 10px!important; padding: 3px 10px!important;
margin: 5px 0px 5px -5px; margin: 25px 0px 5px 0px;
} }
} }

View File

@ -10,8 +10,6 @@ export function http_get(url, type = 'json'){
let data = JSON.parse(xhr.responseText); let data = JSON.parse(xhr.responseText);
if(data.status === 'ok'){ if(data.status === 'ok'){
done(data); done(data);
}else if(data.status === 'redirect'){
if(data.to === 'logout'){location.pathname = "/logout";}
}else{ }else{
err(data); err(data);
} }
@ -22,11 +20,7 @@ export function http_get(url, type = 'json'){
done(xhr.responseText); done(xhr.responseText);
} }
}else{ }else{
if(navigator.onLine === false){ handle_error_response(xhr, err);
err({status: xhr.status, code: "CONNECTION_LOST", message: 'Ooups! Looks like your internet has gone away'});
}else{
err({status: xhr.status, message: xhr.responseText || 'Oups! Something went wrong'});
}
} }
} }
} }
@ -52,8 +46,6 @@ export function http_post(url, data, type = 'json'){
let data = JSON.parse(xhr.responseText); let data = JSON.parse(xhr.responseText);
if(data.status === 'ok'){ if(data.status === 'ok'){
done(data); done(data);
}else if(data.status === 'redirect'){
if(data.to === 'logout'){location.pathname = "/logout";}
}else{ }else{
err(data); err(data);
} }
@ -61,11 +53,7 @@ export function http_post(url, data, type = 'json'){
err({message: 'oups', trace: error}); err({message: 'oups', trace: error});
} }
}else{ }else{
if(navigator.onLine === false){ handle_error_response(xhr, err);
err({status: xhr.status, code: "CONNECTION_LOST", message: 'Connection Lost'});
}else{
err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'});
}
} }
} }
} }
@ -84,8 +72,6 @@ export function http_delete(url){
let data = JSON.parse(xhr.responseText); let data = JSON.parse(xhr.responseText);
if(data.status === 'ok'){ if(data.status === 'ok'){
done(data); done(data);
}else if(data.status === 'redirect'){
if(data.to === 'logout'){location.pathname = "/logout";}
}else{ }else{
err(data); err(data);
} }
@ -93,14 +79,38 @@ export function http_delete(url){
err({message: 'oups', trace: error}); err({message: 'oups', trace: error});
} }
}else{ }else{
if(navigator.onLine === false){ handle_error_response(xhr, err);
err({status: xhr.status, code: "CONNECTION_LOST", message: 'Connection Lost'});
}else{
err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'});
}
} }
} }
} }
xhr.send(null); xhr.send(null);
}); });
} }
function handle_error_response(xhr, err){
let message = (function(content){
let message = content;
try{
message = JSON.parse(content)['message'];
}catch(err){}
return message;
})(xhr.responseText);
if(xhr.status === 500){
err({message: message || "Oups something went wrong with our servers"})
}else if(xhr.status === 401){
if(location.pathname !== '/login'){ location.pathname = "/login"; }
err({message: message || "Authentication error"});
}else if(xhr.status === 403){
err({message: message || "You can\'t do that"});
}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'});
}else{
err({status: xhr.status, message: xhr.responseText || 'Oups something went wrong'});
}
}

View File

@ -3,16 +3,19 @@ import { http_get, http_post, http_delete } from '../helpers/';
class SessionManager{ class SessionManager{
isLogged(){ isLogged(){
let url = '/api/session' let url = '/api/session'
return http_get(url); return http_get(url)
.then(data => data.result);
} }
url(type){ url(type){
if(type === 'dropbox'){ if(type === 'dropbox'){
let url = '/api/session/auth/dropbox'; let url = '/api/session/auth/dropbox';
return http_get(url); return http_get(url)
.then(data => data.result);
}else if(type === 'gdrive'){ }else if(type === 'gdrive'){
let url = '/api/session/auth/gdrive'; let url = '/api/session/auth/gdrive';
return http_get(url); return http_get(url)
.then(data => data.result);
}else{ }else{
return Promise.error({message: 'not authorization backend for: '+type, code: 'UNKNOWN_PROVIDER'}) return Promise.error({message: 'not authorization backend for: '+type, code: 'UNKNOWN_PROVIDER'})
} }
@ -20,12 +23,14 @@ class SessionManager{
authenticate(params){ authenticate(params){
let url = '/api/session'; let url = '/api/session';
return http_post(url, params); return http_post(url, params)
.then(data => data.result);
} }
logout(){ logout(){
let url = '/api/session'; let url = '/api/session';
return http_delete(url); return http_delete(url)
.then(data => data.result);
} }
} }

View File

@ -40,7 +40,7 @@ export class IDE extends React.Component {
<MenuBar title={this.props.filename} download={this.props.url} /> <MenuBar title={this.props.filename} download={this.props.url} />
<Editor onSave={this.save.bind(this)} filename={this.props.filename} content={this.props.content} onChange={this.onContentUpdate.bind(this)} /> <Editor onSave={this.save.bind(this)} filename={this.props.filename} content={this.props.content} onChange={this.onContentUpdate.bind(this)} />
<ReactCSSTransitionGroup transitionName="fab" transitionLeave={true} transitionEnter={true} transitionAppear={true} transitionAppearTimeout="300" transitonEnterTimeout="300" transitionLeaveTimeout="200"> <ReactCSSTransitionGroup transitionName="fab" transitionLeave={true} transitionEnter={true} transitionAppear={true} transitionAppearTimeout={300} transitionEnterTimeout={300} transitionLeaveTimeout={200}>
<NgIf key={this.state.needSaving} cond={this.state.needSaving}> <NgIf key={this.state.needSaving} cond={this.state.needSaving}>
<NgIf cond={!this.props.isSaving}> <NgIf cond={!this.props.isSaving}>
<Fab onClick={this.save.bind(this)}><Icon name="save" style={{height: '100%', width: '100%'}}/></Fab> <Fab onClick={this.save.bind(this)}><Icon name="save" style={{height: '100%', width: '100%'}}/></Fab>

View File

@ -1,5 +1,5 @@
.component_menubar{ .component_menubar{
background: #313538; background: var(--dark);
color: #f1f1f1; color: #f1f1f1;
border-bottom: 1px solid var(--color); border-bottom: 1px solid var(--color);

View File

@ -22,5 +22,5 @@ module.exports = {
clientID: "dropbox_client_id", clientID: "dropbox_client_id",
redirectURI: "application_url/login" redirectURI: "application_url/login"
}, },
secret_key: 'not_so_secret_key' secret_key: process.env.SECRET_KEY || 'not_so_secret_key'
} }

View File

@ -11,7 +11,7 @@ app.use(function(req, res, next){
if(req.cookies.auth !== null){ if(req.cookies.auth !== null){
return next(); return next();
}else{ }else{
return res.send({status: 'redirect', to: 'logout'}); return res.status(401).send({status: "error", message: "You need to authenticate first"});
} }
}); });

View File

@ -24,7 +24,7 @@ app.post('/', function(req, res){
}; };
const cookie = crypto.encrypt(persist); const cookie = crypto.encrypt(persist);
if(Buffer.byteLength(cookie, 'utf-8') > 4096){ if(Buffer.byteLength(cookie, 'utf-8') > 4096){
res.send({status: 'error', message: 'we can\'t authenticate you', }) res.status(413).send({status: 'error', message: 'we can\'t authenticate you', })
}else{ }else{
res.cookie('auth', crypto.encrypt(persist), { maxAge: 365*24*60*60*1000, httpOnly: true, path: "/api/" }); res.cookie('auth', crypto.encrypt(persist), { maxAge: 365*24*60*60*1000, httpOnly: true, path: "/api/" });
res.send({status: 'ok'}); res.send({status: 'ok'});
@ -38,7 +38,7 @@ app.post('/', function(req, res){
} }
return t; return t;
} }
res.send({status: 'error', message: message(err), code: err.code}); res.status(401).send({status: 'error', message: message(err), code: err.code});
}); });
}); });
@ -48,16 +48,16 @@ app.delete('/', function(req, res){
// TODO in May 2019: remove the line below which was inserted to mitigate a cookie migration issue. // TODO in May 2019: remove the line below which was inserted to mitigate a cookie migration issue.
res.clearCookie("auth"); // the issue was a change in the cookie path which would have make res.clearCookie("auth"); // the issue was a change in the cookie path which would have make
// impossible for an existing user to logout // impossible for an existing user to logout
res.send({status: 'ok'}) res.send({status: 'ok'});
}); });
app.get('/auth/:id', function(req, res){ app.get('/auth/:id', function(req, res){
Session.auth({type: req.params.id}) Session.auth({type: req.params.id})
.then((url) => { .then((url) => {
res.send({status: 'ok', result: url}) res.send({status: 'ok', result: url});
}) })
.catch((err) => { .catch((err) => {
res.send({status: 'error', message: 'can\'t get authorization url', trace: err}) res.status(404).send({status: 'error', message: 'can\'t get authorization url', trace: err});
}); });
}); });

View File

@ -1,6 +1,6 @@
const crypto = require('crypto'), const crypto = require('crypto'),
algorithm = 'aes-256-cbc', algorithm = 'aes-256-cbc',
password = require('../../config_server')['secret_key']; password = require('../../config_server')['secret_key'];
module.exports = { module.exports = {
encrypt: function(obj){ encrypt: function(obj){