2 Achegas 063a04fffd ... 15844d82bd

Autor SHA1 Mensaxe Data
  Maxim 15844d82bd auth changes => logOut is done %!s(int64=5) %!d(string=hai) anos
  Maxim 293bffe5b2 somewhat %!s(int64=5) %!d(string=hai) anos

+ 3 - 0
src/actions/auth/signIn.js

@@ -12,5 +12,8 @@ export const signInFailure = error => ({
     type: actionTypes.SIGN_IN_REQUEST_FAILURE,
     error
 });
+export const signInStateZeroing = () => ({
+    type: actionTypes.SIGN_IN_STATE_ZEROING
+})
 
 export default signIn;

+ 10 - 0
src/actions/auth/tokenCheckout.js

@@ -0,0 +1,10 @@
+import * as types from './../../constants/auth';
+
+export const activeToken = payload => ({
+    type: types.ACTIVE_TOKEN,
+    payload
+})
+
+export const inactiveToken = () => ({
+    type: types.INACTIVE_TOKEN
+})

+ 29 - 22
src/components/common/protectedRoute.js

@@ -1,43 +1,50 @@
 import React from 'react';
 import { Route, Redirect } from 'react-router-dom';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import { inactiveToken, activeToken } from './../../actions/auth/tokenCheckout'
 import decode from 'jwt-decode';
 
 import * as routes from './../../constants/routes';
 import storageKey from './../../utils/storageKey';
 
-export default ({ component: Component, tokenAuth, access, ...rest }) => (
+const protectedRoute = ({ component: Component, activeToken, inactiveToken, access, ...rest }) => (
     <Route
         {...rest}
         render={props => {
-            // test
             try {
-                const storagedData = localStorage.getItem(storageKey);
+                const user = decode(localStorage.getItem(storageKey));
+                activeToken(user.role);
+                {/* if (user.exp < Date.now()*1000) {
+                    throw new Error('Date expired');
+                } */}
 
-                if (access === 'public') {
-                    return <Component {...props} />
-                }
-                
-                if (!storagedData || storagedData.exp < Date.now()) {
-                    throw new Error(1);
-                }
-
-                const user = decode(storagedData);
-                const { role } = user; 
-
-                if (access === 'admin-only' && !role) {
-                    throw new Error(2)                   
+                if (access === 'admin-only' && !user.role) {
+                    throw new Error('Permission Denied')
                 }
 
                 return <Component {...props} />
             }
-            catch ({ message: errorCode }) {
-                if (errorCode === '1') {
-                    return <Redirect to={routes.SIGN_IN} />
-                }
-                if (errorCode === '2') {
+            catch ({ message }) {
+                if (message === 'Permission Denied') {
                     return <div>Permission denied</div>
                 }
+
+                inactiveToken();
+
+                if (access === 'public') {
+                    return <Component {...props} />
+                }
+
+                return <Redirect to={routes.SIGN_IN} />
             }
         }}
     />
-)
+)
+
+const mapDispatchToProps = dispatch => bindActionCreators({
+    activeToken,
+    inactiveToken
+}, dispatch);
+
+export default connect(null, mapDispatchToProps)(protectedRoute);

+ 78 - 47
src/components/public/Header.js

@@ -1,67 +1,96 @@
-import React from 'react';
-import { Link } from "react-router-dom";
+import React, { PureComponent } from 'react';
+import { Link, withRouter, Redirect } from "react-router-dom";
 import { connect } from 'react-redux';
 import { bindActionCreators } from 'redux';
+import axios from 'axios';
 
-import * as routes from '../../constants/routes'
-import logOut from './../../actions/auth/logOut';
+import storageKey from './../../utils/storageKey';
+import { signInStateZeroing } from './../../actions/auth/signIn';
+import { LOG_OUT_URL } from './../../constants/auth';
 
-class Header extends React.Component {
+import * as routes from '../../constants/routes';
+
+//pure component + make storage check an action -> connect router
+class Header extends PureComponent {
     handleLogOut = () => {
-        const { logOut } = this.props;
-        logOut()
-        //TODO: REDIRECT
+        console.log(this.props)
+        const { history, zeroState } = this.props;
+
+        axios.get(LOG_OUT_URL, {})
+            .then(() => {
+                console.log('request was succesfull')
+                localStorage.removeItem(storageKey);
+                zeroState();
+                history.push('/sign-in');
+            })
+            .catch(e => alert('Sorry, something bad happened, try log out later \n' + e.message));
     }
 
     render() {
+        const { role } = this.props;
+
         return (
-            <nav class="navbar navbar-expand-lg navbar-dark bg-dark p-3">
-                <Link to={routes.LANDING} class="navbar-brand" href="#">Test.io</Link>
-                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
-                    <span class="navbar-toggler-icon" />
+            <nav className="navbar navbar-expand-lg navbar-dark bg-dark p-3">
+                <Link to={routes.LANDING} className="navbar-brand" href="#">Test.io</Link>
+                <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
+                    <span className="navbar-toggler-icon" />
                 </button>
 
-                <div class="collapse navbar-collapse" id="navbarSupportedContent">
-                    <ul class="navbar-nav mr-auto">
-                        <li class="nav-item active">
-                            <Link onClick={this.handleClick} class="nav-link" to={routes.HOME}>Home <span class="sr-only">(current)</span></Link>
+                <div className="collapse navbar-collapse" id="navbarSupportedContent">
+                    <ul className="navbar-nav mr-auto">
+                        <li className="nav-item active">
+                            <Link onClick={this.handleClick} className="nav-link" to={routes.HOME}>Home <span className="sr-only">(current)</span></Link>
                         </li>
-                        <li class="nav-item">
-                            <Link onClick={this.handleClick} class="nav-link" to={routes.PROFILE}>Profile</Link>
+                        <li className="nav-item">
+                            <Link onClick={this.handleClick} className="nav-link" to={routes.PROFILE}>Profile</Link>
                         </li>
-                        <li class="nav-item">
-                            <Link onClick={this.handleClick} class="nav-link" to={routes.TESTS}>Tests</Link>
+                        <li className="nav-item">
+                            <Link onClick={this.handleClick} className="nav-link" to={routes.TESTS}>Tests</Link>
                         </li>
-                        <li class="nav-item">
-                            <Link onClick={this.handleClick} class="nav-link" to={routes.CATEGORIES}>Catigories</Link>
+                        <li className="nav-item">
+                            <Link onClick={this.handleClick} className="nav-link" to={routes.CATEGORIES}>Catigories</Link>
                         </li>
                         {/* TODO: admin-only ! */}
-                        <li class="nav-item dropdown">
-                            <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-                                Dropdown
+                        {
+                            role
+                                ?
+                                <li className="nav-item dropdown">
+                                    {/* eslint-disable-next-line */}
+                                    <a className="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                        Dropdown
                                 </a>
-                            <div class="dropdown-menu" aria-labelledby="navbarDropdown">
-                                <Link to={routes.CREATE_TEST} className="nav-link text-secondary">Create Test</Link>
-                                <Link to={routes.CREATE_CATEGORY} className="nav-link text-secondary">Create Category</Link>
-
-                                <div class="dropdown-divider"></div>
-                                <a class="nav-link text-secondary" href="#">Something else here</a>
-                            </div>
-                        </li>
+                                    <div className="dropdown-menu" aria-labelledby="navbarDropdown">
+                                        <Link to={routes.CREATE_TEST} className="nav-link text-secondary">Create Test</Link>
+                                        <Link to={routes.CREATE_CATEGORY} className="nav-link text-secondary">Create Category</Link>
 
+                                        <div className="dropdown-divider"></div>
+                                        {/* eslint-disable-next-line */}
+                                        <a className="nav-link text-secondary" href="#">Something else here</a>
+                                    </div>
+                                </li>
+                                :
+                                null
+                        }
                     </ul>
-                    <form class="form-inline my-lg-0">
-                        <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" />
-                        <button class="btn btn-outline-success my-2 my-sm-0 mr-2" type="submit">Search</button>
+                    <form className="form-inline my-lg-0">
+                        <input className="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" />
+                        <button className="btn btn-outline-success my-2 my-sm-0 mr-2" type="submit">Search</button>
                     </form>
-                    <div className="">
-                        <Link to={routes.SIGN_IN}>
-                            <button className="btn btn-outline-primary">Sign In</button>
-                        </Link>
-                        <Link to={routes.SIGN_UP}>
-                            <button className="btn btn-outline-primary mx-2">Sign Up</button>
-                        </Link>
-                        <button onClick={this.handleLogOut} className="btn btn-outline-primary">Log Out</button>
+                    <div className="controls">
+                        {
+                            role !== null
+                                ?
+                                <button onClick={this.handleLogOut} className="btn btn-outline-primary">Log Out</button>
+                                :
+                                <React.Fragment>
+                                    <Link to={routes.SIGN_IN}>
+                                        <button className="btn btn-outline-primary">Sign In</button>
+                                    </Link>
+                                    <Link to={routes.SIGN_UP}>
+                                        <button className="btn btn-outline-primary mx-2">Sign Up</button>
+                                    </Link>
+                                </React.Fragment>
+                        }
                     </div>
                 </div>
             </nav>
@@ -70,8 +99,10 @@ class Header extends React.Component {
 }
 
 const mapStateToProps = state => ({
-    state: state.logOut
+    role: state.tokenCheckout.role,
 });
-const mapDispatchToProps = dispatch => bindActionCreators({ logOut }, dispatch)
+const mapDispatchToProps = dispatch => bindActionCreators({
+    zeroState: signInStateZeroing
+}, dispatch)
 
-export default connect(mapStateToProps, mapDispatchToProps)(Header);
+export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Header));

+ 18 - 18
src/components/public/signIn/index.js

@@ -7,32 +7,32 @@ import { Redirect } from 'react-router-dom'
 import Spinner from './../../common/spinner';
 import signIn from './../../../actions/auth/signIn';
 
+
+
 const SignIn = ({ signIn, user }) => {
-    const someShit = user.isFetching
-        ?
-        <Spinner />
-        : user.data
+    return (
+        user.isFetching
             ?
-            <Redirect to='/' />
-            : user.error
+            <Spinner />
+            : user.data
                 ?
-                (
-                    <React.Fragment>
-                        <Form signIn={signIn} requestError={user.error} />
-                    </React.Fragment>
-
-                )
-                :
-                <Form signIn={signIn} />
-
-    return someShit
-
+                <Redirect to='/' />
+                : user.error
+                    ?
+                    (
+                        <React.Fragment>
+                            <Form signIn={signIn} requestError={user.error} />
+                        </React.Fragment>
+                    )
+                    :
+                    <Form signIn={signIn} />
+    )
 }
 
 
 
 const mapStateToProps = state => ({
-    user: state.signIn
+    user: state.signIn,
 });
 const mapDispatchToProps = dispatch => bindActionCreators({
     signIn

+ 0 - 2
src/components/user/ProfilePage/ChangeLoginForm/index.js

@@ -1,5 +1,4 @@
 import React from 'react';
-import { connect } from 'react-redux';
 import { Field, reduxForm } from 'redux-form';
 
 import formInput from '../../../common/formInput';
@@ -7,7 +6,6 @@ import formInput from '../../../common/formInput';
 class ChangeLoginForm extends React.Component {
 
     render() {
-        // const { children, handleSubmit, handlers: { handleClick, submit } } = this.props;
         return (
             <form  >
                 <Field name="login" type="email" placeholder="Enter new login" component={formInput} />

+ 5 - 6
src/components/user/ProfilePage/index.js

@@ -25,7 +25,6 @@ class ProfilePage extends React.Component {
     }
 
     render() {
-        const { users, userChangeRequest, passwordChangeRequest } = this.props;
         const { data } = user;
         const {clicked} =this.state;
 
@@ -33,30 +32,30 @@ class ProfilePage extends React.Component {
             <div className="page page--bottom-only profile-page">
                 <section className="border bg-autumn-foliage m-5 rounded d-flex">
                     <div>
-                        <img className="m-4 " src=" https://cdn.iconscout.com/icon/free/png-256/avatar-373-456325.png" ></img>
+                        <img className="m-4" src="https://cdn.iconscout.com/icon/free/png-256/avatar-373-456325.png" alt="banner" ></img>
                     </div>
                     <div className="border-left bg-autumn-foliage m-5 rounded w-75 ">
                             {!clicked
                             ?
                         <div  className ="d-flex mb-3" >
-                            <h3 className = " p-2 flex-fill bd-highlight">Name</h3>
+                            <h3 className = "p-2 flex-fill bd-highlight">Name</h3>
                             <h5 className="p-2 font-po-bold flex-fill bd-highlight align-self-end">{data.name}</h5>
                             <button type="button" onClick ={this.handleClick} className=" flex-fill bg-shadow btn btn-outline-light col-3">Change</button>
                         </div>
                         :
                         <div>
-                        <h3 className = " p-2 flex-fill bd-highlight">Name</h3>
+                        <h3 className="p-2 flex-fill bd-highlight">Name</h3>
                         <h5 className="p-2 font-po-bold flex-fill bd-highlight align-self-end">{data.name}</h5>
                         <ChangeLoginForm> </ChangeLoginForm>
                         </div>
                             }
                         <div  className ="d-flex mb-3" >
-                            <h3 className = "p-2 flex-fill bd-highlight">Email</h3>
+                            <h3 className="p-2 flex-fill bd-highlight">Email</h3>
                             <h5 className="p-2 flex-fill font-po-bold bd-highlight align-self-end">{data.email}</h5>
                             <button type="button" className=" flex-fill bg-shadow btn btn-outline-light col-3">Change</button>
                         </div>
                         <div  className ="d-flex mb-3" >
-                            <h3 className = "p-2 flex-fill bd-highlight">Password</h3>
+                            <h3 className="p-2 flex-fill bd-highlight">Password</h3>
                             <button type="button" className=" flex-fill bg-shadow btn btn-outline-light col-3">Change</button>
                         </div>
                  

+ 5 - 1
src/constants/auth.js

@@ -2,6 +2,7 @@ export const SIGN_IN_URL = 'https://test-app-a-level.herokuapp.com/auth/login';
 export const SIGN_IN_REQUEST = 'SIGN_IN_REQUEST';
 export const SIGN_IN_REQUEST_SUCCESS = 'SIGN_IN_REQUEST_SUCCESS';
 export const SIGN_IN_REQUEST_FAILURE = 'SIGN_IN_REQUEST_FAILURE';
+export const SIGN_IN_STATE_ZEROING = 'SIGN_IN_STATE_ZEROING';
 
 export const SIGN_UP_URL = 'https://test-app-a-level.herokuapp.com/auth/register';
 export const SIGN_UP_REQUEST = 'SIGN_UP_REQUEST';
@@ -11,4 +12,7 @@ export const SIGN_UP_REQUEST_FAILURE = 'SIGN_UP_REQUEST_FAILURE';
 export const LOG_OUT_URL = 'https://test-app-a-level.herokuapp.com/auth/logout';
 export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
 export const LOG_OUT_REQUEST_SUCCESS = 'LOG_OUT_REQUEST_SUCCESS';
-export const LOG_OUT_REQUEST_FAILURE = 'LOG_OUT_REQUEST_FAILURE';
+export const LOG_OUT_REQUEST_FAILURE = 'LOG_OUT_REQUEST_FAILURE';
+
+export const ACTIVE_TOKEN = 'ACTIVE_TOKEN';
+export const INACTIVE_TOKEN = 'INACTIVE_TOKEN'; 

+ 1 - 1
src/reducers/auth/logOut.js

@@ -1,7 +1,7 @@
 import * as actionTypes from './../../constants/auth';
 import initialState from './../initialState';
 
-export default function signInReducer(state = initialState.signIn, {type, error}) {
+export default function signInReducer(state = initialState.logOut, {type, error}) {
 
     switch (type) {
         case actionTypes.LOG_OUT_REQUEST: {

+ 4 - 3
src/reducers/auth/signIn.js

@@ -11,23 +11,24 @@ export default function signInReducer(state = initialState.signIn, {payload, typ
             }
         }
         case actionTypes.SIGN_IN_REQUEST_SUCCESS: {
-            const { user, token } = payload;
+            const { user } = payload;
 
             return {
                 ...state,
                 isFetching: false,
                 data: user,
-                token
             }
         }
         case actionTypes.SIGN_IN_REQUEST_FAILURE: {
-
             return {
                 ...state,
                 isFetching: false,
                 error
             }
         }
+        case actionTypes.SIGN_IN_STATE_ZEROING: {
+            return initialState.signIn
+        }
         default: {
             return state;
         }

+ 22 - 0
src/reducers/auth/tokenCheckout.js

@@ -0,0 +1,22 @@
+import * as actionTypes from './../../constants/auth';
+import initialState from './../initialState';
+
+export default function signInReducer(state = initialState.tokenCheckout, {type, payload: role, error}) {
+    switch(type) {
+        case actionTypes.ACTIVE_TOKEN: {
+            return {
+                ...state,
+                role
+            }
+        }
+        case actionTypes.INACTIVE_TOKEN: {
+            return {
+                ...state,
+                role: null
+            }
+        }
+        default: {  
+            return state;
+        }
+    }
+}

+ 4 - 0
src/reducers/index.js

@@ -3,8 +3,12 @@ import { reducer as form } from 'redux-form';
 
 import signIn from './auth/signIn';
 import signUp from './auth/signUp';
+import logOut from './auth/logOut';
+import tokenCheckout from './auth/tokenCheckout';
 
 export default combineReducers({
+    tokenCheckout,
+    logOut,
     signIn,
     signUp,
     form

+ 3 - 0
src/reducers/initialState.js

@@ -14,5 +14,8 @@ export default {
         isFetching: false,
         isSuccessful: null,
         error: null
+    },
+    tokenCheckout: {
+        role: null
     }
 }

+ 21 - 36
src/router.js

@@ -1,6 +1,5 @@
-import React, { Suspense, lazy } from "react";
-import { Switch, Route, withRouter } from "react-router-dom";
-import { connect } from 'react-redux';
+import React, { Suspense } from "react";
+import { Switch } from "react-router-dom";
 
 import ProtectedRoute from './components/common/protectedRoute';
 import config from './configs/routerConfig';
@@ -8,37 +7,23 @@ import config from './configs/routerConfig';
 import Header from './components/public/Header';
 import Spinner from './components/common/spinner';
 
-import { bindActionCreators } from "redux";
+const router = props => (
+    <div className="app">
+        <Header />
+        <Suspense fallback={<Spinner />}>
+            <Switch>
+                {config.map(route =>
+                    <ProtectedRoute
+                        path={route.path}
+                        component={route.component}
+                        access={route.access}
+                        key={route.path}
+                        exact
+                    />
+                )}
+            </Switch>
+        </Suspense>
+    </div>
+)
 
-class Router extends React.Component {
-    render() {
-        const { user, tokenAuth } = this.props;
-        // TODO: add footer
-        return (
-            <div className="app">
-                <Header />
-                <Suspense fallback={<Spinner />}>
-                    <Switch>
-                        {config.map(route =>
-                            <ProtectedRoute
-                                path={route.path}
-                                component={route.component}
-                                access={route.access}
-                                user={user}
-                                key={route}
-                                exact
-                            />
-                        )}
-                    </Switch>
-                </Suspense>
-            </div>
-        )
-    }
-}
-
-const mapStateToProps = state => ({
-    user: state.signIn
-})
-// const mapDispatchToProps = dispatch => bindActionCreators({ tokenAuth }, dispatch);
-
-export default withRouter(connect(mapStateToProps, null)(Router));
+export default router;

+ 2 - 0
src/state/index.js

@@ -17,6 +17,8 @@ const store = createStore(
     applyMiddleware(sagaMiddleware, logger)
 );
 
+window.state = () => store.getState()
+
 sagaMiddleware.run(sagas);
 
 export default store;