Merge pull request #1 from apuc/authentication
added server auth and protected routes
This commit is contained in:
		
							
								
								
									
										76
									
								
								src/App.js
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								src/App.js
									
									
									
									
									
								
							| @@ -1,60 +1,40 @@ | ||||
| import React, { Suspense, lazy } from 'react'; | ||||
| import { HashRouter as Router, Route, Switch } from 'react-router-dom'; | ||||
| import { useSelector } from 'react-redux'; | ||||
| import 'bootstrap/dist/css/bootstrap.min.css'; | ||||
| import './fonts/stylesheet.css'; | ||||
| import { selectAuth } from './redux/outstaffingSlice'; | ||||
| import React, { Suspense, lazy } from 'react' | ||||
| import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' | ||||
| import 'bootstrap/dist/css/bootstrap.min.css' | ||||
| import './fonts/stylesheet.css' | ||||
| import { ProtectedRoute } from './components/ProtectedRoute/ProtectedRoute'; | ||||
|  | ||||
| const AuthPageForDevelopers = lazy(() => import('./pages/AuthPageForDevelopers')); | ||||
| // const AuthPageForPartners = lazy(() => import('./pages/AuthPageForPartners')); | ||||
| const HomePage = lazy(() => import('./pages/HomePage')); | ||||
| const CandidatePage = lazy(() => import('./pages/CandidatePage')); | ||||
| const CalendarPage = lazy(() => import('./pages/CalendarPage')); | ||||
| const ReportPage = lazy(() => import('./pages/ReportFormPage.js')); | ||||
| const FormPage = lazy(() => import('./pages/FormPage.js')); | ||||
| const AuthPageForDevelopers = lazy(() => | ||||
|   import('./pages/AuthPageForDevelopers') | ||||
| ) | ||||
| const AuthPageForPartners = lazy(() => import('./pages/AuthPageForPartners')) | ||||
| const HomePage = lazy(() => import('./pages/HomePage')) | ||||
| const CandidatePage = lazy(() => import('./pages/CandidatePage')) | ||||
| const CalendarPage = lazy(() => import('./pages/CalendarPage')) | ||||
| const ReportPage = lazy(() => import('./pages/ReportFormPage.js')) | ||||
| const FormPage = lazy(() => import('./pages/FormPage.js')) | ||||
|  | ||||
| const App = () => { | ||||
|   const isAuth = useSelector(selectAuth); | ||||
|   // const [candidateForCalendar, setCandidateForCalendar] = useState([]); | ||||
|  | ||||
|   // const getCandidateForCalendar = (candidate) => { | ||||
|   //   console.log('candidate ', candidate); | ||||
|   //   setCandidateForCalendar(candidate); | ||||
|   // }; | ||||
|  | ||||
|   return ( | ||||
|     <Router> | ||||
|       <Suspense fallback={<div>Loading...</div>}> | ||||
|         {isAuth ? ( | ||||
|           <Switch> | ||||
|             <Route path="/" exact> | ||||
|               <HomePage /> | ||||
|             </Route> | ||||
|             <Route path="/candidate/:id"> | ||||
|               <CandidatePage /> | ||||
|             </Route> | ||||
|             <Route path="/calendar"> | ||||
|               <CalendarPage /> | ||||
|             </Route> | ||||
|             <Route path="/form"> | ||||
|               <FormPage /> | ||||
|             </Route> | ||||
|             <Route path="/report"> | ||||
|               <ReportPage /> | ||||
|             </Route> | ||||
|             <Route> | ||||
|               <div>Not found page</div> | ||||
|             </Route> | ||||
|           </Switch> | ||||
|         ) : ( | ||||
|           <Route path="/" exact> | ||||
|         <Switch> | ||||
|           <Route path='/auth' exact> | ||||
|             {/* <AuthPageForPartners /> */} | ||||
|             <AuthPageForDevelopers /> | ||||
|           </Route> | ||||
|         )} | ||||
|           <ProtectedRoute path='/' exact component={HomePage} /> | ||||
|           <ProtectedRoute path='/candidate/:id' component={CandidatePage} /> | ||||
|           <ProtectedRoute path='/calendar' component={CalendarPage} /> | ||||
|           <ProtectedRoute path='/form' component={FormPage} /> | ||||
|           <ProtectedRoute path='/report' component={ReportPage} /> | ||||
|           <Route> | ||||
|             <div>Page not found</div> | ||||
|           </Route> | ||||
|         </Switch> | ||||
|       </Suspense> | ||||
|     </Router> | ||||
|   ); | ||||
| }; | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export default App; | ||||
| export default App | ||||
|   | ||||
| @@ -1,56 +1,83 @@ | ||||
| import React from 'react'; | ||||
| import { useDispatch } from 'react-redux'; | ||||
| import { auth } from '../../redux/outstaffingSlice'; | ||||
| import style from './AuthForDevelopers.module.css'; | ||||
| import ellipse from '../../images/ellipse.png'; | ||||
| import arrow from '../../images/arrow__login_page.png'; | ||||
| import authImg from '../../images/auth_img.png'; | ||||
| import cross from '../../images/cross.png'; | ||||
| import text from '../../images/Body_Text.png'; | ||||
| import align from '../../images/align-left.png'; | ||||
| import phone from '../../images/phone.png'; | ||||
| import telegram from '../../images/telegram.png'; | ||||
| import vector from '../../images/Vector_Smart_Object.png'; | ||||
| import vectorBlack from '../../images/Vector_Smart_Object_black.png'; | ||||
| import React, { useState } from 'react' | ||||
| import { useDispatch } from 'react-redux' | ||||
| import { auth } from '../../redux/outstaffingSlice' | ||||
| import style from './AuthForDevelopers.module.css' | ||||
| import ellipse from '../../images/ellipse.png' | ||||
| import arrow from '../../images/arrow__login_page.png' | ||||
| import authImg from '../../images/auth_img.png' | ||||
| import cross from '../../images/cross.png' | ||||
| import text from '../../images/Body_Text.png' | ||||
| import align from '../../images/align-left.png' | ||||
| import phone from '../../images/phone.png' | ||||
| import telegram from '../../images/telegram.png' | ||||
| import vector from '../../images/Vector_Smart_Object.png' | ||||
| import vectorBlack from '../../images/Vector_Smart_Object_black.png' | ||||
| import { fetchAuth } from '../../server/server' | ||||
|  | ||||
| const AuthForDevelopers = () => { | ||||
|   const dispatch = useDispatch(); | ||||
|   const dispatch = useDispatch() | ||||
|  | ||||
|   const [username, setUsername] = useState('') | ||||
|   const [password, setPassword] = useState('') | ||||
|  | ||||
|   return ( | ||||
|     <section className={style.developers}> | ||||
|       <div className={style.developers__background}> | ||||
|         <img className={style.vector} src={vector} alt="" /> | ||||
|         <img className={style.vectorBlack} src={vectorBlack} alt="" /> | ||||
|         <div className="container"> | ||||
|           <div className="row"> | ||||
|             <div className="col-12 col-xl-6"> | ||||
|         <img className={style.vector} src={vector} alt='' /> | ||||
|         <img className={style.vectorBlack} src={vectorBlack} alt='' /> | ||||
|         <div className='container'> | ||||
|           <div className='row'> | ||||
|             <div className='col-12 col-xl-6'> | ||||
|               <div className={style.developers__box}> | ||||
|                 <h2 className={style.developers__title}> | ||||
|                   Войти в <span>систему</span> | ||||
|                 </h2> | ||||
|                 <div className={style.developers__partners}> | ||||
|                   <img src={ellipse} alt="" /> | ||||
|                   <img src={ellipse} alt='' /> | ||||
|                   <span>Для разработчиков</span> | ||||
|                 </div> | ||||
|                 <form className={style.developers__form}> | ||||
|                   <label htmlFor="login">Ваш логин:</label> | ||||
|                   <input id="login" type="text" placeholder="Логин" /> | ||||
|                   <label htmlFor='login'>Ваш логин:</label> | ||||
|                   <input | ||||
|                     id='login' | ||||
|                     type='text' | ||||
|                     placeholder='Логин' | ||||
|                     value={username} | ||||
|                     onChange={(e) => setUsername(e.target.value)} | ||||
|                   /> | ||||
|  | ||||
|                   <label htmlFor="password">Пароль:</label> | ||||
|                   <input id="password" type="password" placeholder="Пароль" /> | ||||
|                   <label htmlFor='password'>Пароль:</label> | ||||
|                   <input | ||||
|                     id='password' | ||||
|                     type='password' | ||||
|                     placeholder='Пароль' | ||||
|                     value={password} | ||||
|                     onChange={(e) => setPassword(e.target.value)} | ||||
|                   /> | ||||
|  | ||||
|                   <button className={style.form__btn} type="submit" onClick={() => dispatch(auth(true))}> | ||||
|                   <button | ||||
|                     className={style.form__btn} | ||||
|                     type='submit' | ||||
|                     onClick={() =>  | ||||
|                       fetchAuth({ | ||||
|                         username, | ||||
|                         password, | ||||
|                         dispatch: ()=> dispatch(auth(true)) | ||||
|                       }) | ||||
|                     } | ||||
|                   > | ||||
|                     Войти | ||||
|                   </button> | ||||
|                 </form> | ||||
|               </div> | ||||
|             </div> | ||||
|             <div className="col-xl-2"> | ||||
|               <img className={style.developers__arrow} src={arrow} alt="" /> | ||||
|             <div className='col-xl-2'> | ||||
|               <img className={style.developers__arrow} src={arrow} alt='' /> | ||||
|             </div> | ||||
|             <div className="col-12 col-xl-4"> | ||||
|             <div className='col-12 col-xl-4'> | ||||
|               <div className={style.developers__info}> | ||||
|                 <div className={style.developers__info__box}> | ||||
|                   <img src={authImg} alt="" /> | ||||
|                   <img src={authImg} alt='' /> | ||||
|                   <h3> | ||||
|                     Управление | ||||
|                     <br /> командой | ||||
| @@ -60,7 +87,7 @@ const AuthForDevelopers = () => { | ||||
|                 <div className={style.developers__info__container}> | ||||
|                   <div className={style.developers__info__img}> | ||||
|                     <div> | ||||
|                       <img className="cross" src={cross} alt="" /> | ||||
|                       <img className='cross' src={cross} alt='' /> | ||||
|                     </div> | ||||
|                     <div> | ||||
|                       {/* <img className={style.specialists} src={specialists} alt="" /> */} | ||||
| @@ -69,39 +96,44 @@ const AuthForDevelopers = () => { | ||||
|                   </div> | ||||
|  | ||||
|                   <ul className={style.info__list}> | ||||
|                     <li className={style.info__list__item}>Рабочее пространство</li> | ||||
|                     <li className={style.info__list__item}>Управление задачами</li> | ||||
|                     <li className={style.info__list__item}> | ||||
|                       Рабочее пространство | ||||
|                     </li> | ||||
|                     <li className={style.info__list__item}> | ||||
|                       Управление задачами | ||||
|                     </li> | ||||
|                   </ul> | ||||
|                 </div> | ||||
|  | ||||
|                 <img className={style.img__text} src={text} alt="" /> | ||||
|                 <img className={style.img__text} src={text} alt='' /> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
|           <div className="row"> | ||||
|             <div className="col-12 col-xl-7"> | ||||
|           <div className='row'> | ||||
|             <div className='col-12 col-xl-7'> | ||||
|               <div className={style.developers__footer__left}> | ||||
|                 <div className={style.footer__left__img}> | ||||
|                   <img src={align} alt="" /> | ||||
|                   <img src={align} alt='' /> | ||||
|                 </div> | ||||
|                 <div className={style.footer__left__sp}> | ||||
|                   <span> | ||||
|                     © Адвего — биржа контента №1. Копирайтинг, рерайтинг, переводы, работа на дому: поставщик | ||||
|                     уникального контента. 2021{' '} | ||||
|                     © Адвего — биржа контента №1. Копирайтинг, рерайтинг, | ||||
|                     переводы, работа на дому: поставщик уникального контента. | ||||
|                     2021{' '} | ||||
|                   </span> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|  | ||||
|             <div className="col-4 col-xl-2"> | ||||
|             <div className='col-4 col-xl-2'> | ||||
|               <div className={style.developers__footer__icon}> | ||||
|                 <img src={phone} alt="" /> | ||||
|                 <img src={telegram} alt="" /> | ||||
|                 <img src={phone} alt='' /> | ||||
|                 <img src={telegram} alt='' /> | ||||
|               </div> | ||||
|             </div> | ||||
|  | ||||
|             <div className="col-8 col-xl-3"> | ||||
|             <div className='col-8 col-xl-3'> | ||||
|               <div className={style.developers__footer__right}> | ||||
|                 <p className={style.phone}>+7 495 156 78 98</p> | ||||
|                 <p className={style.workingHours}>Будни с 9:00 до 21:00</p> | ||||
| @@ -111,7 +143,7 @@ const AuthForDevelopers = () => { | ||||
|         </div> | ||||
|       </div> | ||||
|     </section> | ||||
|   ); | ||||
| }; | ||||
|   ) | ||||
| } | ||||
|  | ||||
| export default AuthForDevelopers; | ||||
| export default AuthForDevelopers | ||||
|   | ||||
| @@ -19,7 +19,7 @@ const Calendar = () => { | ||||
|  | ||||
|   const { name, skillsName } = candidateForCalendar; | ||||
|  | ||||
|   const abbreviatedName = name.substring(0, name.lastIndexOf(' ')); | ||||
|   const abbreviatedName = name && name.substring(0, name.lastIndexOf(' ')); | ||||
|  | ||||
|   return ( | ||||
|     <section className={style.calendar}> | ||||
|   | ||||
							
								
								
									
										23
									
								
								src/components/ProtectedRoute/ProtectedRoute.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/components/ProtectedRoute/ProtectedRoute.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| import React from 'react'; | ||||
| import { useSelector } from 'react-redux' | ||||
| import { Route, Redirect } from 'react-router-dom'; | ||||
| import { selectAuth } from '../../redux/outstaffingSlice'; | ||||
|  | ||||
| export const ProtectedRoute = ({ component: Component, ...rest }) => { | ||||
|     const isAuth = useSelector(selectAuth) | ||||
|     const existingToken = localStorage.getItem('auth_token') | ||||
|     const expiresAt = localStorage.getItem('access_token_expired_at') | ||||
|  | ||||
|     const isTokenAlive = (existingToken && expiresAt && new Date(expiresAt).getTime() > (new Date()).getTime()); | ||||
|      | ||||
|     return ( | ||||
|         <Route | ||||
|             {...rest} | ||||
|             render={props => | ||||
|             ( isAuth || isTokenAlive) ? ( | ||||
|                 <Component {...props} /> | ||||
|             ) : <Redirect to='/auth' /> | ||||
|             } | ||||
|         /> | ||||
|     ); | ||||
|   } | ||||
| @@ -6,7 +6,7 @@ const initialState = { | ||||
|   filteredCandidates: [], | ||||
|   selectedItems: [], | ||||
|   currentCandidate: {}, | ||||
|   auth: true, | ||||
|   auth: false, | ||||
| }; | ||||
|  | ||||
| export const outstaffingSlice = createSlice({ | ||||
|   | ||||
| @@ -1,40 +1,73 @@ | ||||
| export const fetchProfile = async (link, index) => { | ||||
|   try { | ||||
|     const response = await fetch(`${link}${index}`); | ||||
|     let data = await response.json(); | ||||
|     const response = await fetch(`${link}${index}`) | ||||
|     let data = await response.json() | ||||
|  | ||||
|     return data; | ||||
|     return data | ||||
|   } catch (error) {} | ||||
| }; | ||||
| } | ||||
|  | ||||
| export const fetchSkills = async (link) => { | ||||
|   try { | ||||
|     const response = await fetch(link); | ||||
|     let data = await response.json(); | ||||
|     const response = await fetch(link) | ||||
|     let data = await response.json() | ||||
|  | ||||
|     return data; | ||||
|     return data | ||||
|   } catch (error) {} | ||||
| }; | ||||
| } | ||||
|  | ||||
| export const fetchItemsForId = async (link, id) => { | ||||
|   try { | ||||
|     const response = await fetch(`${link}${id}`); | ||||
|     let data = await response.json(); | ||||
|     const response = await fetch(`${link}${id}`) | ||||
|     let data = await response.json() | ||||
|  | ||||
|     return data; | ||||
|     return data | ||||
|   } catch (error) {} | ||||
| }; | ||||
| } | ||||
|  | ||||
| export const fetchForm = async (link, info) => { | ||||
|   try { | ||||
|     const response = await fetch(link, { | ||||
|       method: 'POST', | ||||
|       headers: { | ||||
|         'Content-Type': 'multipart/form-data', | ||||
|         'Content-Type': 'multipart/form-data' | ||||
|       }, | ||||
|       body: info, | ||||
|     }); | ||||
|       body: info | ||||
|     }) | ||||
|  | ||||
|     return response; | ||||
|     return response | ||||
|   } catch (error) {} | ||||
| }; | ||||
| } | ||||
|  | ||||
| export const fetchAuth = async ({ username, password, dispatch }) => { | ||||
|   try { | ||||
|     const response = await fetch( | ||||
|       'https://guild.craft-group.xyz/api/user/login', | ||||
|       { | ||||
|         method: 'POST', | ||||
|         mode: 'cors', | ||||
|         headers: { | ||||
|           'Content-Type': 'application/json' | ||||
|         }, | ||||
|         body: JSON.stringify({ | ||||
|           username, | ||||
|           password | ||||
|         }) | ||||
|       } | ||||
|     ) | ||||
|  | ||||
|     if(!response.ok) { | ||||
|       return response.statusText; | ||||
|     } | ||||
|  | ||||
|     response | ||||
|       .json() | ||||
|       .then((resJSON) => { | ||||
|         localStorage.setItem('auth_token', resJSON.access_token) | ||||
|         localStorage.setItem('access_token_expired_at', resJSON.access_token_expired_at) | ||||
|         dispatch(); | ||||
|       }) | ||||
|   } catch (error) { | ||||
|     console.error('Error occured: ', error) | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 GitHub
						GitHub