diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2018-09-16 22:40:05 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2018-09-16 22:40:05 +0200 |
| commit | d3e4bb3ed2585859a3adeb7eeff35b7c75ebd840 (patch) | |
| tree | e88e9edae5a63328fb1acc625e5624990717d20f /app/client/auth | |
| parent | 189be96150fbd49766228cf50c6a89279542565c (diff) | |
auth gate on main app. pull in auth routes from bucky.
Diffstat (limited to 'app/client/auth')
| -rw-r--r-- | app/client/auth/auth.actions.js | 82 | ||||
| -rw-r--r-- | app/client/auth/auth.gate.js | 41 | ||||
| -rw-r--r-- | app/client/auth/auth.reducer.js | 82 | ||||
| -rw-r--r-- | app/client/auth/index.js | 11 | ||||
| -rw-r--r-- | app/client/auth/login.component.js | 86 | ||||
| -rw-r--r-- | app/client/auth/logout.component.js | 24 | ||||
| -rw-r--r-- | app/client/auth/signup.component.js | 101 |
7 files changed, 427 insertions, 0 deletions
diff --git a/app/client/auth/auth.actions.js b/app/client/auth/auth.actions.js new file mode 100644 index 0000000..5968f87 --- /dev/null +++ b/app/client/auth/auth.actions.js @@ -0,0 +1,82 @@ +import * as types from '../types'; + +export const setToken = (data) => { + return { type: types.auth.set_token, data } +} +export const setError = (data) => { + return { type: types.auth.set_error, data } +} +export const setCurrentUser = (data) => { + return { type: types.auth.set_current_user, data } +} +export function logout() { + return { type: types.auth.logout_user }; +} +export function authLoading() { + return { type: types.auth.loading }; +} + +export function InvalidCredentialsException(message) { + this.message = message; + this.name = 'InvalidCredentialsException'; +} + +export function login(username, password) { + return (dispatch) => { + dispatch(authLoading()); + apiClient() + .post(api.GET_TOKEN, { + username, + password + }) + .then(function (response) { + dispatch(setToken(response.data.token)); + dispatch(getCurrentUser()); + }) + .catch(function (error) { + dispatch(setError(true)); + if (error.response.status === 400) { + throw new InvalidCredentialsException(error); + } + throw error; + }); + }; +} + +export function signup(data) { + return (dispatch) => { + dispatch(authLoading()); + apiClient() + .post(api.SIGNUP, data) + .then(function (response) { + console.log(response.data); + dispatch(login(data.username, data.password)); + }) + .catch(function (error) { + console.log(error) + if (error.response.status === 400) { + // dispatch(accountError("There was an error creating your account.")) + throw new InvalidCredentialsException(error); + } + throw error; + }); + }; +} + +export function getCurrentUser() { + return (dispatch) => { + dispatch(authLoading()); + apiClient() + .get(api.CURRENT_USER) + .then(function (response) { + dispatch(setCurrentUser(response.data)); + console.log('set current user') + }) + .catch(function (error) { + if (error.response.status === 400) { + throw new InvalidCredentialsException(error); + } + throw error; + }); + }; +} diff --git a/app/client/auth/auth.gate.js b/app/client/auth/auth.gate.js new file mode 100644 index 0000000..e7a9940 --- /dev/null +++ b/app/client/auth/auth.gate.js @@ -0,0 +1,41 @@ +import { h, Component } from 'preact'; +// import PropTypes from 'prop-types'; +import { BrowserRouter, Route } from 'react-router-dom' +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { Redirect } from 'react-router-dom'; + +import Login from './login.component'; +import Logout from './logout.component'; +import Signup from './signup.component'; + +import { randint } from '../util/math' + +class AuthGate extends Component { + render(){ + if (this.props.auth.isAuthenticated) return children + return ( + <BrowserRouter> + <div> + <div className="spinfx"></div> + <Route exact path='/' component={Login} /> + <Route exact path='/login' component={Login} /> + <Route exact path='/logout' component={Logout} /> + <Route exact path='/signup' component={Signup} /> + </div> + </BrowserRouter> + ) + } + componentDidMount(){ + document.querySelector('.spinfx').style.backgroundImage = 'linear-gradient(' + (randint(40)-5) + 'deg, #fde, #ffe)' + } +} + +const mapStateToProps = (state) => ({ + auth: state.auth +}); + +const mapDispatchToProps = (dispatch) => ({ +}); + +export default connect(mapStateToProps, mapDispatchToProps)(AuthGate); diff --git a/app/client/auth/auth.reducer.js b/app/client/auth/auth.reducer.js new file mode 100644 index 0000000..cacb0d5 --- /dev/null +++ b/app/client/auth/auth.reducer.js @@ -0,0 +1,82 @@ +import types from '../types'; + +const authInitialState = { + token: null, + user: {}, + groups: {}, + loading: false, + isAuthenticated: false, +}; + +const auth = (state = authInitialState, action) => { + switch(action.type) { + case types.auth.set_token: + return { + ...state, + token: action.data, + isAuthenticated: !!action.data, + loading: false, + error: null, + }; + + case types.auth.loading: + return { + ...state, + loading: true, + error: null, + }; + + case types.auth.set_current_user: + const groups = {} + action.data.groups.forEach(g => groups[g.name.toLowerCase()] = true) + if (action.data.is_staff) { + groups['staff'] = true + } + if (action.data.is_superuser) { + groups['superuser'] = true + } + return { + ...state, + user: action.data, + groups, + error: null, + }; + + case types.auth.logout_user: + return { + ...authInitialState + }; + + case types.auth.set_error: + return { + ...state, + loading: false, + error: action.data, + } + + case types.auth.loading: + // const initial_state_el = document.querySelector('#initial_state') + // if (initial_state_el) { + // try { + // const initial_state = JSON.parse(initial_state_el.innerHTML) + // if (initial_state && initial_state.auth && initial_state.auth.user) { + // console.log(initial_state.auth.user) + // return { + // ...state, + // user: { + // ...initial_state.auth.user, + // } + // } + // } + // } catch (e) { + // console.error("error loading initial state") + // } + // } + return state; + + default: + return state; + } +} + +export default auth; diff --git a/app/client/auth/index.js b/app/client/auth/index.js new file mode 100644 index 0000000..5e6b2b0 --- /dev/null +++ b/app/client/auth/index.js @@ -0,0 +1,11 @@ +import Gate from './auth.gate'; +import Login from './login.component'; +import Logout from './logout.component'; +import Signup from './signup.component'; + +export default { + Gate, + Login, + Logout, + Signup, +}
\ No newline at end of file diff --git a/app/client/auth/login.component.js b/app/client/auth/login.component.js new file mode 100644 index 0000000..4ffab34 --- /dev/null +++ b/app/client/auth/login.component.js @@ -0,0 +1,86 @@ +import { h, Component } from 'preact'; +// import PropTypes from 'prop-types'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { Redirect } from 'react-router-dom'; +// import { Link } from 'react-router-dom'; +import * as authActions from './auth.actions'; + +import { Group, Param, TextInput, Button } from '../common'; + +class Login extends Component { + state = { + username: '', + password: '', + } + constructor() { + super() + this.handleChange = this.handleChange.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + } + handleChange(e) { + const name = e.target.name + const value = e.target.value + this.setState({ + [name]: value, + error: null, + }) + } + handleSubmit(e) { + e.preventDefault() + this.props.actions.login(this.state.username, this.state.password) + } + render(){ + if (this.props.auth.isAuthenticated) { + return <Redirect to="/" /> + } + return ( + <form onSubmit={this.handleSubmit}> + <h1>Log in</h1><br /> + <Group> + <TextInput + autofocus + autocapitalize="off" + autocomplete="off" + title="Username" + name="username" + type="text" + value={this.state.username} + onChange={this.handleChange} + /> + <TextInput + title="Password" + name="password" + type="password" + value={this.state.password} + onChange={this.handleChange} + /> + <Button + loading={this.props.auth.loading} + > + Login + </Button> + {this.renderAuthError()} + </Group> + </form> + ) + } + renderAuthError(){ + if (this.props.auth.error) { + return ( + <div className='form-input-hint'>{"There was an error logging you in (bad password?)"}</div> + ) + } + return null + } +} + +const mapStateToProps = (state) => ({ + auth: state.auth, +}); + +const mapDispatchToProps = (dispatch) => ({ + actions: bindActionCreators(authActions, dispatch) +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Login); diff --git a/app/client/auth/logout.component.js b/app/client/auth/logout.component.js new file mode 100644 index 0000000..bcc3bce --- /dev/null +++ b/app/client/auth/logout.component.js @@ -0,0 +1,24 @@ +import { h, Component } from 'preact'; +// import PropTypes from 'prop-types'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { Redirect } from 'react-router-dom'; +import * as authActions from './auth.actions'; + +class Logout extends Component { + componentWillMount(props){ + this.props.actions.logout() + } + render(){ + return <Redirect to="/" /> + } +} + +const mapStateToProps = (state) => ({ +}); + +const mapDispatchToProps = (dispatch) => ({ + actions: bindActionCreators(authActions, dispatch) +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Logout); diff --git a/app/client/auth/signup.component.js b/app/client/auth/signup.component.js new file mode 100644 index 0000000..c86d31b --- /dev/null +++ b/app/client/auth/signup.component.js @@ -0,0 +1,101 @@ +import { h, Component } from 'preact'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { Redirect } from 'react-router-dom'; +import actions from './auth.actions'; + +import { Group, Param, TextInput, Button } from '../common'; + +class Signup extends Component { + state = { + username: '', + password: '', + password2: '', + } + constructor() { + super() + this.handleChange = this.handleChange.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + } + handleChange(e) { + const name = e.target.name + const value = e.target.value + this.setState({ + [name]: value, + error: null, + }) + } + validate(){ + if (!this.state.password || this.state.password !== this.state.password2) { + return false + } + return true + } + handleSubmit(e) { + e.preventDefault() + if (!this.validate) { + return this.props.actions.setError('bad password') + } + this.props.actions.signup(this.state) + } + render(){ + if (this.props.auth.isAuthenticated) { + return <Redirect to="/" /> + } + return ( + <form onSubmit={this.handleSubmit}> + <h1>New account</h1><br /> + <Group> + <TextInput + autofocus + autocapitalize="off" + autocomplete="off" + title="Username" + name="username" + type="text" + value={this.state.username} + onChange={this.handleChange} + /> + <TextInput + title="Password" + name="password" + type="password" + value={this.state.password} + onChange={this.handleChange} + /> + <TextInput + title="Password again :)" + name="password2" + type="password" + value={this.state.password2} + onChange={this.handleChange} + /> + <Button + loading={this.props.auth.loading} + > + Login + </Button> + {this.renderAuthError()} + </Group> + </form> + ) + } + renderAuthError(){ + if (this.props.auth.error) { + return ( + <div className='form-input-hint'>{"Please doublecheck the form"}</div> + ) + } + return null + } +} + +const mapStateToProps = (state) => ({ + auth: state.auth, +}); + +const mapDispatchToProps = (dispatch) => ({ + actions: bindActionCreators({ ...actions }, dispatch) +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Signup); |
