Browse Source

[ADD] login page

Gogs 7 years ago
parent
commit
b2fa3c5122

+ 16 - 6
src/actions/index.js

@@ -1,6 +1,6 @@
-import axios from 'axios'
+// import axios from 'axios'
 import { isArray } from 'lodash'
-import { API_URL } from '../constants/ResourceNames'
+// import { API_URL } from '../constants/ResourceNames'
 import http from '../utils/http'
 import { 
     REQUEST_START,
@@ -34,6 +34,13 @@ export const notify = message => dispatch  => {
     })
 }
 
+/**
+ * 
+ */
+export const checkLogin = () => dispatch => {
+    console.log('e')
+}
+
 /**
  * 
  * @param {*} resource 
@@ -48,7 +55,7 @@ export const get = resource => async dispatch => {
         const response = await http.get(`${resource}`)
         dispatch(ok(response.data))
     } catch (error) {
-        dispatch(ko(resource, error))
+        dispatch(ko(resource, error.response))
     }
 }
 
@@ -60,13 +67,15 @@ export const post = (resources, data) => async dispatch => {
     dispatch({
         type: REQUEST_START
     })
-
+    
     data = data || {}
 
+
     if (isArray(resources)) {
         for (let r of resources) {
             try {
-                const response = await axios.post(`${API_URL}${r}`, data)
+                // const response = await axios.post(`${API_URL}${r}`, data)
+                const response = await http.post(`${r}`, data)
                 dispatch(ok(response.data))
             } catch (error) {
                 dispatch(ko(r, error))
@@ -74,7 +83,8 @@ export const post = (resources, data) => async dispatch => {
         }
     } else {
         try {
-            const response = await axios.post(`${API_URL}${resources}`, data)
+            // const response = await axios.post(`${API_URL}${resources}`, data)
+            const response = await http.post(`${resources}`, data)
             dispatch(ok(response.data))
         } catch (error) {
             dispatch(ko(resources, error))

+ 34 - 3
src/components/common/Routes.js

@@ -1,17 +1,48 @@
 import React, { Component } from 'react'
-import { Route } from 'react-router-dom'
+import { connect } from 'react-redux'
+import { Route, Redirect, withRouter } from 'react-router-dom'
+import Login from '../pages/Login'
 import routes from '../../routes'
 
 class Routes extends Component {
     render() {
+        const { isAuthenticated } = this.props
+
         return (
             <div>
                 {routes.map((route, index) =>
-                    <Route key={index} path={route.path} render={() => <route.component title={route.title} />} />
+                    <Route 
+                        key={index} 
+                        path={route.path} 
+                        render={props => isAuthenticated ? (
+                                <route.component title={route.title} />
+                            ) : (
+                                <Redirect to={{
+                                    pathname: '/login', 
+                                    state: { 
+                                        from: props.location 
+                                    }
+                                }} 
+                            />)
+                        } 
+                    />
                 )}
+                <Route path='/login' component={Login} />
             </div>
         )
     }
 }
 
-export default Routes
+/**
+ * 
+ * @param {*} state 
+ * @param {*} props 
+ */
+const mapStateToProps = (state, props) => {
+    return {
+        isAuthenticated: state.app.isAuthenticated
+    }
+}
+
+
+export default withRouter(connect(mapStateToProps)(Routes))

+ 136 - 6
src/components/pages/Login.js

@@ -1,26 +1,118 @@
 import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { Redirect } from 'react-router-dom'
 import Paper from 'material-ui/Paper'
+import Typography from 'material-ui/Typography'
 import TextField from 'material-ui/TextField'
+import Button from 'material-ui/Button'
+import { AUTH } from '../../constants/ResourceNames'
+import { post } from '../../actions'
+import { getToken } from '../../utils/auth'
 import { withStyles } from 'material-ui/styles'
 
 const styles = theme => ({
     root: {
+        position: 'absolute',
+        width: '100%',
+        height: '100%',
         display: 'flex',
         alignItems: 'center',
         justifyContent: 'center'
+    },
+    paper: {
+        padding: theme.spacing.unit * 3
+    },
+    formHeading: {
+        marginTop: theme.spacing.unit * 3,
+        marginBottom: theme.spacing.unit * 2
+    },
+    form: {
+        display: 'flex',
+        flexDirection: 'column',
+        width: 350,
+        height: 230
+    },
+    txtField: {
+        margin: theme.spacing.unit * 2
+    },
+    submitBtn: {
+        marginTop: theme.spacing.unit * 3
     }
 })
 
 class Login extends Component {
+    /**
+     * 
+     * @param {*} props 
+     */
+    constructor(props) {
+        super(props)
+
+        this.state = {
+            username: '',
+            password: '',
+            hasError: false,
+            redirectToReferrer: false
+        }
+    }
+
+    /**
+     * 
+     */
+    componentWillMount() {
+        this.props.check()
+    }
+
+    /**
+     * 
+     */
+    handleChangeInput = e => {
+        this.setState({
+            [e.target.id]: e.target.value
+        }, this.handleChangeState)
+    }
+
+    /**
+     * 
+     */
+    handleChangeState = () => {
+        // console.log(this.state)
+    }
+
+    /**
+     * 
+     */
     render() {
-        const { classes } = this.props
+        const { from } = this.props.location.state || { from: { pathname: '/' }}
+        const { isAuthenticated, submit, classes } = this.props
+        const { username, password, hasError } = this.state
+
+        if (isAuthenticated) {
+            return <Redirect to={from} />
+        }
 
         return (
             <div className={classes.root}>
-                <Paper className={classes.root} elevation={4}>
-                    <form>
-                        <TextField label='Usuario' autoComplete='off' />
-                        <TextField label='Contraseña' type='password' autoComplete='off' />
+                <Paper className={classes.paper} elevation={4}>
+                    <Typography className={classes.formHeading} variant='display1' color='primary' align='center'>Eiru Automation</Typography>
+                    <form className={classes.form} noValidate autoComplete='off'>
+                        <TextField
+                            id='username'
+                            className={classes.txtField} 
+                            error={hasError}
+                            onKeyUp={this.handleChangeInput}
+                            label='Usuario'
+                            autoFocus 
+                        />
+                        <TextField 
+                            id='password'
+                            className={classes.txtField} 
+                            error={hasError}
+                            onKeyUp={this.handleChangeInput}
+                            label='Contraseña' 
+                            type='password' 
+                        />
+                        <Button className={classes.submitBtn} variant='raised' color='primary' onClick={e => submit(e, username, password)}>Acceder</Button>
                     </form>
                 </Paper>
             </div>
@@ -28,4 +120,42 @@ class Login extends Component {
     }
 }
 
-export default withStyles(styles)(Login)
+/**
+ * 
+ * @param {*} state 
+ * @param {*} props 
+ */
+const mapStateToProps = (state, props) => {
+    return {
+        isAuthenticated: state.app.isAuthenticated
+    }
+}
+
+/**
+ * 
+ * @param {*} dispatch 
+ * @param {*} props 
+ */
+const mapDispatchToProps = (dispatch, props) => ({
+    check() {
+        const token = getToken()
+
+        if (!token) {
+            return
+        }
+
+        dispatch(post(`${AUTH}check_token/`, { token }))
+    },
+    submit(e, username, password) {
+        console.log(username, password)
+
+        dispatch(post(`${AUTH}get_token/`, {
+            username,
+            password
+        }))
+    }
+})
+
+Login = withStyles(styles)(Login)
+
+export default connect(mapStateToProps, mapDispatchToProps)(Login)

+ 2 - 2
src/components/pages/TasksList.js

@@ -144,9 +144,9 @@ class TasksList extends Component {
                             label='Nombre del sistema'
                             type='text' 
                             autoComplete='off' 
+                            onKeyUp={this.handleChangeName}
                             autoFocus 
                             fullWidth
-                            onKeyUp={this.handleChangeName}
                          />
                         <TextField
                             id='nameConfirmation'
@@ -154,9 +154,9 @@ class TasksList extends Component {
                             label='Repita el nombre del sistema'
                             type='text' 
                             autoComplete='off' 
-                            fullWidth
                             error={!isDialogValid}
                             onKeyUp={this.handleChangeName}
+                            fullWidth
                          />
                     </DialogContent>
                     <DialogActions>

+ 5 - 0
src/constants/ResourceNames.js

@@ -3,6 +3,11 @@
  */
 export const API_URL = 'http://192.168.88.100:8000/api/v1/'
 
+/**
+ * 
+ */
+export const AUTH = 'auth/'
+
 /** 
  * 
  */

+ 0 - 2
src/index.js

@@ -2,9 +2,7 @@ import React from 'react'
 import ReactDOM from 'react-dom'
 import App from './components/App'
 import registerServiceWorker from './registerServiceWorker'
-import registerAuthEngine from './registerAuthEngine'
 import 'typeface-roboto'
 
 ReactDOM.render(<App />, document.getElementById('root'));
-registerAuthEngine();
 registerServiceWorker();

+ 38 - 2
src/reducers/app.js

@@ -1,4 +1,5 @@
 import { isEqual, has, isString } from 'lodash'
+import { setToken } from '../utils/auth'
 import {
     REQUEST_START,
     REQUEST_KO, 
@@ -21,7 +22,8 @@ const initialState = {
     dialog: {
         isOpen: false,
         message: null
-    }
+    },
+    isAuthenticated: false
 }
 
 /**
@@ -41,6 +43,25 @@ export const app = (state = initialState, action) => {
     }
 
     if (isEqual(action.type, HIDE_SPINNER) || isEqual(action.type, REQUEST_OK)) {
+        // Response is auth
+        if (has(action.payload, 'auth')) {
+            state = {
+                ...state,
+                isAuthenticated: action.payload.auth
+            }
+        }
+
+        // Response has token
+        if (has(action.payload, 'token')) {
+            setToken(action.payload.token)
+
+            state = {
+                ...state,
+                isAuthenticated: true
+            }
+        }
+
+        // Response is action
         if (has(action.payload, 'action')) {
             if (isEqual(action.payload.action.type, 'redirect')) {
                 const params = {
@@ -52,6 +73,7 @@ export const app = (state = initialState, action) => {
             }
         }   
         
+        // Hide spinner
         state = {
             ...state,
             spinner: {
@@ -61,7 +83,21 @@ export const app = (state = initialState, action) => {
         }
     }
 
-    if (isEqual(action.type, SHOW_NOTIFICATION) || isEqual(action.type, REQUEST_KO)) {
+    if (isEqual(action.type, REQUEST_KO)) {
+        const status = action.payload.status
+        
+        if (status === 401) {
+            return {
+                ...state,
+                spinner: {
+                    isOpen: false
+                },
+                isAuthenticated: false
+            }
+        }
+    }
+
+    if (isEqual(action.type, SHOW_NOTIFICATION)) {
         state = {
             ...state,
             spinner: {

+ 0 - 5
src/routes/index.js

@@ -1,4 +1,3 @@
-import Login from '../components/pages/Login'
 import Dashboard from '../components/pages/Dashboard'
 import ContainersList from '../components/pages/ContainersList'
 import RequestsList from '../components/pages/RequestsList'
@@ -16,10 +15,6 @@ import About from '../components/pages/About'
 // import AboutIcon from '../components/icons/AboutIcon'
 
 const routes = [
-    {
-        path: '/login',
-        component: Login
-    },
     {
         path: '/dashboard',
         component: Dashboard,

+ 29 - 0
src/utils/auth.js

@@ -0,0 +1,29 @@
+import { isEmpty  } from 'lodash'
+
+const TOKEN_KEY = 'eiruAutomation'
+
+/**
+ * 
+ */
+export const hasToken = () => {
+    return isEmpty(getToken())
+}
+
+/**
+ * 
+ */
+export const getToken = () => {
+    return localStorage.getItem(TOKEN_KEY)
+}
+
+/**
+ * 
+ * @param {*} token 
+ */
+export const setToken = token => {
+    if (!token.startsWith('JWT')) {
+        token = `JWT ${token}`
+    }
+
+    localStorage.setItem(TOKEN_KEY, token)
+}