Skip to content

Commit

Permalink
Žiga Franko Feedback
Browse files Browse the repository at this point in the history
* packages update

* JWT token authentication prepared.

- Defined access and refresh tokens
- Prepared interceptor for obtaining new access token or redirecting to the login page when refresh token is expired

* urls.ts is meant to contain all the urs that the app will have. Each page has defined custom variable URL

* IPFS and IPFSList components redefined

* BaseLayout modified

* user context state logic moved into state/user.ts with some minor modifications

* Login component updated, it uses some basic logic to login.

Available email: [email protected], password: geslo123

* urls applied in NavigationBar

* ProtectedRoute replaced with Page component, which ensures that all his defined routes are protected by checking if user is logged in and if we have access token defined

* Error404 page initialized

* Authentication and api setup is now in api/api.ts

* App updated with different user store, urls applied, and some routes moved into Page component

* .eslintignore added <- I can not handle lint every save there is made (cpu -> 700%). Add * to disable lint.

* IPFS pql/test action fixed

* Adding keys for each ipfs in list. Hashes are used for keys

* Baselayout margin changed to padding

* Redirect to error page where the un-know ipfs hash is opened

* IPFS removed from this issue

Co-authored-by: Žiga Franko <[email protected]>
  • Loading branch information
ZiyedB and Frenkiee authored Feb 28, 2021
1 parent e116fc7 commit 665b350
Show file tree
Hide file tree
Showing 22 changed files with 236 additions and 208 deletions.
Empty file added .eslintignore
Empty file.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module.exports = {
ignorePatterns: ['.eslintrc.js'],
// Rules can be here to override the preset of eslint from airbnb, if they are too strict.
rules: {
camelcase: 'warn',
'@typescript-eslint/no-empty-function': 'warn',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
Expand Down
34 changes: 17 additions & 17 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"react-scripts": "4.0.1",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^2.0.3",
"typescript": "^4.1.3",
"typescript": "^4.2.2",
"web-vitals": "^0.2.4",
"yup": "^0.32.8"
},
Expand Down Expand Up @@ -75,7 +75,7 @@
]
},
"lint-staged": {
"*.{js,tsx}": "eslint --fix"
"*.{js,ts,tsx}": "eslint --fix"
},
"husky": {
"hooks": {
Expand Down
41 changes: 15 additions & 26 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
import React, { useState } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import './App.scss';
import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-dom';
import BaseLayout from './components/layouts/BaseLayout';
import { storageNames } from './config';
import UserContext from './hooks/UserContext';
import Ipfs from './pages/ipfs/Ipfs';
import IpfsList from './pages/ipfs/IpfsList';
import Login from './pages/Login/Login';
import { ProtectedRoute } from './shared/ProtectedRoute';
import Login from './components/pages/auth/Login';
import Page from './components/Page';
import { ERROR_404_PAGE, LOGIN_PAGE, USER_PAGES } from './components/urls';
import Error404 from './components/pages/errors/Error404';
import UserContext, { emptyUser } from './state/user';
import './App.scss';

function App(): JSX.Element {
// Handle user state
const accessToken = localStorage.getItem(storageNames.user) || '';
const [isLoggedIn, setLoggedIn] = useState(!!accessToken);

// Handle user login by setting the storage and state
const login = (token: string): void => {
setLoggedIn(true);
localStorage.setItem(storageNames.user, token);
};
const [user, setUser] = useState({ ...emptyUser });

// Handle user logout
const logout = (): void => {
setLoggedIn(false);
localStorage.removeItem(storageNames.user);
};
const login = (email: string): void => setUser({ email, isLoggedIn: true });
const logout = (): void => setUser({ ...emptyUser });

return (
<UserContext.Provider value={{ isLoggedIn, login, logout }}>
<UserContext.Provider value={{ user, login, logout }}>
<Router>
<BaseLayout>
<Switch>
<Route path="/login" component={Login} />
<ProtectedRoute path="/ipfs/:hash" component={Ipfs} />
<ProtectedRoute path={['/', '/ipfs']} component={IpfsList} />
<Route path={LOGIN_PAGE} component={Login} />
<Route path={ERROR_404_PAGE} component={Error404} />
<Route path={USER_PAGES} component={Page} />
<Route path="/" render={() => <Redirect to={ERROR_404_PAGE} />} />
</Switch>
</BaseLayout>
</Router>
Expand Down
70 changes: 70 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import axios from 'axios';

const OBTAIN_TOKEN = '/token/obtain/';
// const REFRESH_TOKEN_URL = '/token/refresh/';

export const ACCESS_TOKEN = 'access_token';
export const REFRESH_TOKEN = 'refresh_token';

interface JWTTokenResponse {
refresh: string;
access: string;
}

export const axiosInstance = axios.create({
baseURL: 'http://127.0.0.1:7424/api/',
timeout: 5000,
headers: {
// 'Authorization': localStorage.getItem('access_token') ? 'JWT ' + localStorage.getItem('access_token') : null,
'Content-Type': 'application/json',
accept: 'application/json',
},
});

export const saveTokens = (jwtToken: JWTTokenResponse): JWTTokenResponse => {
localStorage.setItem(ACCESS_TOKEN, jwtToken.access);
localStorage.setItem(REFRESH_TOKEN, jwtToken.refresh);
return jwtToken;
};

const localGet = (key: string, defaultValue = ''): string => {
const value = localStorage.getItem(key);
if (value === null) return defaultValue;
return value;
};

export const getAccessToken = (): string => localGet(ACCESS_TOKEN);
export const getRefreshToken = (): string => localGet(REFRESH_TOKEN);

axiosInstance.interceptors.response.use(
(res) => res,
(err) => {
// const originalRequest = err.config;
// If refresh tokens is expired redirect to login page
// if (err.response.status === 401 && originalRequest.url === REFRESH_TOKEN_URL) {
// window.location.href = '/login/';
// return Promise.reject(err);
// }

// // If access token is expired update it
// if (err.response.status === 401 && err.response.statusText === 'Unauthorized') {
// return axiosInstance
// .post<JWTTokenResponse>(REFRESH_TOKEN_URL, {refresh: localStorage.getItem(REFRESH_TOKEN)})
// .then(res => res.data)
// .then(saveTokens)
// .then(res => {
// axiosInstance.defaults.headers['Authorization'] = 'JWT ' + res.access;
// originalRequest.headers['Authorization'] = 'JWT ' + res.access;

// return axiosInstance(originalRequest);
// })
// }

return Promise.reject(err);
},
);

export const obtainTokenApi = async (email: string, password: string): Promise<JWTTokenResponse> =>
axiosInstance
.post<JWTTokenResponse>(OBTAIN_TOKEN, { email, password })
.then((res) => res.data);
24 changes: 24 additions & 0 deletions src/api/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// import { axiosInstance, obtainTokenApi, saveTokens } from "../api"

// const LOGIN_URL = '';

export interface UserAuthenticationData {
email: string;
password: string;
}

const loginUser = (data: UserAuthenticationData): Promise<void> => {
const { email, password } = data;

if (email !== '[email protected]') throw new Error('Email does not exist!');
if (password !== 'geslo123') throw new Error('Password is incorrect!');
// axiosInstance.post<{}>(LOGIN_URL, data)
// .then((res) => res.data);
return Promise.resolve();
};

export const authenticateUser = async (data: UserAuthenticationData): Promise<void> => {
await loginUser(data);
// const jwtTokens = await obtainTokenApi(data.email, data.password);
// saveTokens(jwtTokens);
};
21 changes: 21 additions & 0 deletions src/components/Page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { useContext } from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import Ipfs from '../pages/ipfs/Ipfs';
import IpfsList from '../pages/ipfs/IpfsList';
import UserContext from '../state/user';
import { IPFS_BASE_PAGE, IPFS_PAGE, LOGIN_PAGE } from './urls';

const Page = (): JSX.Element => {
const { user } = useContext(UserContext);

if (!user.isLoggedIn) return <Redirect to={LOGIN_PAGE} />;

return (
<Switch>
<Route exact path={IPFS_BASE_PAGE} component={IpfsList} />
<Route path={IPFS_PAGE} component={Ipfs} />
</Switch>
);
};

export default Page;
22 changes: 9 additions & 13 deletions src/components/layouts/BaseLayout/BaseLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@ import React from 'react';
import { Container } from 'semantic-ui-react';
import { NavigationBar } from '../NavigationBar';

interface BaseLayoutProps {
children: any;
}
const BaseLayout: React.FC<{}> = ({ children }) => (
<div>
<NavigationBar />

export default function BaseLayout(props: BaseLayoutProps): JSX.Element {
return (
<div>
<NavigationBar />
<Container textAlign="left" style={{ paddingTop: '7em' }}>
{children}
</Container>
</div>
);

<Container textAlign="left" style={{ marginTop: '7em' }}>
{props.children}
</Container>
</div>
);
}
export default BaseLayout;
17 changes: 7 additions & 10 deletions src/components/layouts/NavigationBar/NavigationBar.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
import React, { useContext } from 'react';
import { FaUserCircle } from 'react-icons/fa';
import { Link } from 'react-router-dom';
import UserContext from '../../../hooks/UserContext';
import UserContext from '../../../state/user';
import { HOME_PAGE, IPFS_BASE_PAGE } from '../../urls';
import './NavigationBar.scss';

const NavigationBar = (): JSX.Element => {
const userContext = useContext(UserContext);

const logout = (): void => {
userContext.logout();
};
const { user, logout } = useContext(UserContext);

return (
<div className="navigation-bar">
<nav className="navigation-bar__nav">
<div className="navigation-bar__general">
<Link className="navigation-bar__logo" to="/">
<Link className="navigation-bar__logo" to={HOME_PAGE}>
<img className="navigation-bar__logo-img" src="/assets/images/logo-icon.png" alt="Paralink Network" />
</Link>
<div className="navigation-bar__content">
<ul className="navigation-bar__links">
<li className="navigation-bar__link">
<Link to="/ipfs">Paralink Network</Link>
<Link to={IPFS_BASE_PAGE}>Paralink Network</Link>
</li>
<li className="navigation-bar__link">
<Link to="/ipfs">IPFS</Link>
<Link to={IPFS_BASE_PAGE}>IPFS</Link>
</li>
</ul>
</div>
</div>
{userContext.isLoggedIn ? (
{user.isLoggedIn ? (
<div className="navigation-bar__user">
{/* TODO: Need to have this as a small menu showing up */}
<a className="navigation-bar__link navigation-bar__user-link" onClick={logout} href="/login">
Expand Down
File renamed without changes.
Loading

0 comments on commit 665b350

Please sign in to comment.