Handle and show login/signup errors

This commit is contained in:
Tom Russell 2018-09-13 16:58:05 +01:00
parent 8ac687e454
commit 1ea6b0b75b
7 changed files with 75 additions and 35 deletions

View File

@ -30,6 +30,8 @@ class App extends React.Component {
} }
login(user) { login(user) {
console.log("Logging in")
console.log(user)
this.setState({user: user}); this.setState({user: user});
} }

View File

@ -0,0 +1,20 @@
import React, { Fragment } from 'react';
const ErrorBox = (props) => (
<Fragment>
{
(props.msg)?
(
<div className="alert alert-danger" role="alert">
{
(typeof props.msg === 'string' || props.msg instanceof String)?
props.msg
: 'Unexpected error'
}
</div>
) : null
}
</Fragment>
);
export default ErrorBox;

View File

@ -1,13 +1,16 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Redirect, Link } from 'react-router-dom'; import { Redirect, Link } from 'react-router-dom';
import ErrorBox from './error-box';
class Login extends Component { class Login extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
username: '', username: '',
password: '', password: '',
show_password: '' show_password: '',
error: undefined
}; };
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
@ -26,7 +29,6 @@ class Login extends Component {
handleSubmit(event) { handleSubmit(event) {
event.preventDefault(); event.preventDefault();
const login = this.props.login
fetch('/login', { fetch('/login', {
method: 'POST', method: 'POST',
body: JSON.stringify(this.state), body: JSON.stringify(this.state),
@ -37,20 +39,19 @@ class Login extends Component {
res => res.json() res => res.json()
).then(function(res){ ).then(function(res){
if (res.error) { if (res.error) {
console.error(res.error); // tell user this.setState({error: res.error})
} else { } else {
console.log(res); // redirect back this.setState({error: undefined})
fetch('/users/me').then( fetch('/users/me').then(
(res) => res.json() (res) => res.json()
).then(function(user){ ).then(
console.log(user) (user) => this.props.login(user)
login(user); ).catch(
}).catch(function(err){ (err) => this.setState({error: err})
console.error(err); )
})
} }
}).catch( }.bind(this)).catch(
err => console.error(err) (err) => this.setState({error: err})
); );
} }
@ -62,6 +63,7 @@ class Login extends Component {
<article> <article>
<section className="main-col"> <section className="main-col">
<h1 className="h2">Log in</h1> <h1 className="h2">Log in</h1>
<ErrorBox msg={this.state.error} />
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<label htmlFor="username">Username*</label> <label htmlFor="username">Username*</label>
<input name="username" id="username" <input name="username" id="username"
@ -80,18 +82,18 @@ class Login extends Component {
<div className="form-check"> <div className="form-check">
<input id="show_password" name="show_password" <input id="show_password" name="show_password"
className="position-static" type="checkbox" className="form-check-input" type="checkbox"
checked={this.state.show_password} checked={this.state.show_password}
onChange={this.handleChange} onChange={this.handleChange}
/> />
<label htmlFor="show_password">Show password?</label> <label htmlFor="show_password" className="form-check-label">Show password?</label>
</div> </div>
<div className="buttons-container"> <div className="buttons-container">
<input type="submit" value="Log In" className="btn btn-primary" /> <input type="submit" value="Log In" className="btn btn-primary" />
</div> </div>
Would you like to create an account? Would you like to create an account instead?
<div className="buttons-container"> <div className="buttons-container">
<Link to="sign-up.html" className="btn btn-outline-dark">Sign Up</Link> <Link to="sign-up.html" className="btn btn-outline-dark">Sign Up</Link>

View File

@ -1,6 +1,8 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Redirect, Link } from 'react-router-dom'; import { Redirect, Link } from 'react-router-dom';
import ErrorBox from './error-box';
class SignUp extends Component { class SignUp extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -10,7 +12,8 @@ class SignUp extends Component {
confirm_email: '', confirm_email: '',
password: '', password: '',
show_password: '', show_password: '',
confirm_conditions: false confirm_conditions: false,
error: undefined
}; };
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
@ -39,17 +42,19 @@ class SignUp extends Component {
res => res.json() res => res.json()
).then(function(res){ ).then(function(res){
if (res.error) { if (res.error) {
console.error(res.error); // tell user this.setState({error: res.error})
} else { } else {
console.log(res); // redirect back this.setState({error: undefined})
fetch('/users/me').then(function(user){ fetch('/users/me').then(
this.props.login(user); (res) => res.json()
}).catch(function(err){ ).then(
console.error(err); (user) => this.props.login(user)
}) ).catch(
(err) => this.setState({error: err})
)
} }
}).catch( }.bind(this)).catch(
err => console.error(err) (err) => this.setState({error: err})
); );
} }
@ -64,6 +69,7 @@ class SignUp extends Component {
<p> <p>
Create an account to start colouring in. Create an account to start colouring in.
</p> </p>
<ErrorBox msg={this.state.error} />
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<label htmlFor="username">Username*</label> <label htmlFor="username">Username*</label>
<input name="username" id="username" <input name="username" id="username"
@ -95,20 +101,22 @@ class SignUp extends Component {
<div className="form-check"> <div className="form-check">
<input id="show_password" name="show_password" <input id="show_password" name="show_password"
className="position-static" type="checkbox" className="form-check-input" type="checkbox"
checked={this.state.show_password} checked={this.state.show_password}
onChange={this.handleChange} onChange={this.handleChange}
/> />
<label htmlFor="show_password">Show password?</label> <label className="form-check-label" htmlFor="show_password">
Show password?
</label>
</div> </div>
<div className="form-check"> <div className="form-check">
<input id="confirm_conditions" name="confirm_conditions" <input id="confirm_conditions" name="confirm_conditions"
className="position-static" type="checkbox" className="form-check-input" type="checkbox"
checked={this.state.confirm_conditions} checked={this.state.confirm_conditions}
onChange={this.handleChange} onChange={this.handleChange}
required /> required />
<label htmlFor="confirm_conditions"> <label className="form-check-label" htmlFor="confirm_conditions">
I confirm that I have read and agree to the <a I confirm that I have read and agree to the <a
href="/privacy-policy">privacy policy</a> and <a href="/privacy-policy">privacy policy</a> and <a
href="/user-agreement">contributor agreement</a>. href="/user-agreement">contributor agreement</a>.

View File

@ -12,8 +12,8 @@ input[type="email"] {
input[type="number"] { input[type="number"] {
padding-right: 0.25rem; padding-right: 0.25rem;
} }
.form-check { .form-check-input {
padding-left: 0; margin-top: 0.6rem;
} }
label { label {
margin: 0.5em 0 0; margin: 0.5em 0 0;

View File

@ -178,11 +178,13 @@ server.post('/users', function(req, res){
const user = req.body; const user = req.body;
if (req.session.user_id) { if (req.session.user_id) {
res.send({error: 'Already signed in'}); res.send({error: 'Already signed in'});
return
} }
if (user.email){ if (user.email){
if (user.email != user.confirm_email) { if (user.email != user.confirm_email) {
res.send({error: "Email did not match confirmation."}); res.send({error: "Email did not match confirmation."});
return
} }
} else { } else {
user.email = null; user.email = null;
@ -198,7 +200,7 @@ server.post('/users', function(req, res){
} }
}).catch(function(err){ }).catch(function(err){
console.error(err); console.error(err);
res.send({error: 'Server error'}) res.send(err)
}); });
}); });

View File

@ -2,6 +2,12 @@ import { query } from './db';
function createUser(user) { function createUser(user) {
if (!user.password || user.password.length < 8) {
return Promise.reject({error: 'Password must be at least 8 characters'})
}
if (user.password.length > 70) {
return Promise.reject({error: 'Password must be at most 70 characters'})
}
return query( return query(
`INSERT `INSERT
INTO users ( INTO users (
@ -52,14 +58,14 @@ function authUser(username, password) {
] ]
).then(function(data){ ).then(function(data){
const user = data.rows[0]; const user = data.rows[0];
if (user.auth_ok) { if (user && user.auth_ok) {
return {user_id: user.user_id} return {user_id: user.user_id}
} else { } else {
return {error: 'Authentication failed'} return {error: 'Username or password not recognised'}
} }
}).catch(function(err){ }).catch(function(err){
console.error(err); console.error(err);
return {error: 'Database error'}; return {error: 'Username or password not recognised'};
}) })
} }