mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-21 13:23:05 +01:00
Auth error handling. Auth redirecting
This commit is contained in:
@@ -2,19 +2,30 @@ import { Fragment, useContext, useEffect } from 'react';
|
|||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
import { Navbar } from './components/Navigation/Navbar';
|
import { Navbar } from './components/Navigation/Navbar';
|
||||||
import { Editor, Home, Snippet, Snippets, Auth, Profile } from './containers';
|
import { Editor, Home, Snippet, Snippets, Auth, Profile } from './containers';
|
||||||
import { ProtectedRoute } from './utils';
|
import { decodeToken, ProtectedRoute } from './utils';
|
||||||
import { AuthContext } from './store';
|
import { AuthContext, SnippetsContext } from './store';
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
const { autoLogin } = useContext(AuthContext);
|
const { autoLogin, logout } = useContext(AuthContext);
|
||||||
|
const { getSnippets, countTags } = useContext(SnippetsContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// autoLogin();
|
autoLogin();
|
||||||
// const checker = setInterval(() => {
|
|
||||||
// autoLogin();
|
getSnippets();
|
||||||
// console.log('cake');
|
countTags();
|
||||||
// }, 1000);
|
|
||||||
// return () => window.clearInterval(checker);
|
const checkTokenValidity = setInterval(() => {
|
||||||
|
if (localStorage.token) {
|
||||||
|
const { exp: expiresAt } = decodeToken(localStorage.token);
|
||||||
|
|
||||||
|
if (Date.now() > expiresAt * 1000) {
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => window.clearInterval(checkTokenValidity);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useContext, useEffect } from 'react';
|
import { useContext, useEffect } from 'react';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory, useLocation } from 'react-router';
|
||||||
import { AuthContext } from '../store';
|
import { AuthContext } from '../store';
|
||||||
import { AuthForm } from '../components/Auth';
|
import { AuthForm } from '../components/Auth';
|
||||||
import { Card, Layout } from '../components/UI';
|
import { Card, Layout } from '../components/UI';
|
||||||
@@ -8,9 +8,13 @@ export const Auth = (): JSX.Element => {
|
|||||||
const { isAuthenticated } = useContext(AuthContext);
|
const { isAuthenticated } = useContext(AuthContext);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
// Get previous location
|
||||||
|
const location = useLocation<{ from: string }>();
|
||||||
|
const { from } = location.state || '/';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
history.push('/');
|
history.push(from);
|
||||||
}
|
}
|
||||||
}, [isAuthenticated]);
|
}, [isAuthenticated]);
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ export const ProtectedRoute = ({ ...rest }: RouteProps) => {
|
|||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
return <Route {...rest} />;
|
return <Route {...rest} />;
|
||||||
} else {
|
} else {
|
||||||
return <Redirect to='/auth' />;
|
return (
|
||||||
|
<Redirect
|
||||||
|
to={{
|
||||||
|
pathname: '/auth',
|
||||||
|
state: { from: window.location.pathname }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
20
client/src/utils/authErrorHandler.ts
Normal file
20
client/src/utils/authErrorHandler.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { errorHandler } from '.';
|
||||||
|
import { UserWithRole } from '../typescript/interfaces';
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
err: any;
|
||||||
|
setIsAuthenticated: (v: React.SetStateAction<boolean>) => void;
|
||||||
|
setUser: (v: React.SetStateAction<UserWithRole | null>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authErrorHandler = (params: Params) => {
|
||||||
|
const { err, setUser, setIsAuthenticated } = params;
|
||||||
|
|
||||||
|
errorHandler(err);
|
||||||
|
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
|
||||||
|
setUser(null);
|
||||||
|
|
||||||
|
setIsAuthenticated(false);
|
||||||
|
};
|
||||||
@@ -11,5 +11,7 @@ export const errorHandler = (err: any) => {
|
|||||||
msg = 'Something went wrong';
|
msg = 'Something went wrong';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: emit notification
|
||||||
|
// redirect on error
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,4 +3,6 @@ export * from './badgeColor';
|
|||||||
export * from './findLanguage';
|
export * from './findLanguage';
|
||||||
export * from './searchParser';
|
export * from './searchParser';
|
||||||
export * from './ProtectedRoute';
|
export * from './ProtectedRoute';
|
||||||
|
export * from './authErrorHandler';
|
||||||
export * from './errorHandler';
|
export * from './errorHandler';
|
||||||
|
export * from './decodeToken';
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ services:
|
|||||||
- /path/to/host/data:/app/data
|
- /path/to/host/data:/app/data
|
||||||
ports:
|
ports:
|
||||||
- 5000:5000
|
- 5000:5000
|
||||||
|
environment:
|
||||||
|
- NODE_ENV: production
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
13
list.md
Normal file
13
list.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
**Implemented**
|
||||||
|
|
||||||
|
- [x] Login / logout / register
|
||||||
|
- [x] Auto login (token valid 14 days)
|
||||||
|
- [x] Roles (admin, user)
|
||||||
|
|
||||||
|
**Planned features**
|
||||||
|
|
||||||
|
- [ ] Guest access (read and search access to snippets marked as public)
|
||||||
|
- [ ] Generate token for raw snippet access
|
||||||
|
- [ ] Admin panel (right now admin and users share the same view)
|
||||||
|
|
||||||
|
By default, admin account is created with login `admin@local.com` and password `snippet-admin`. If there are any snippets in the database, they will be assigned to the admin as the owner.
|
||||||
@@ -11,6 +11,20 @@ export const errorHandler = (
|
|||||||
) => {
|
) => {
|
||||||
logger.log(err.message, 'ERROR');
|
logger.log(err.message, 'ERROR');
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV == 'development') {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = {
|
||||||
|
...err
|
||||||
|
};
|
||||||
|
|
||||||
|
if (err.message == 'Validation error') {
|
||||||
|
// @ts-ignore
|
||||||
|
error.message = err.errors[0].message;
|
||||||
|
error.statusCode = 400;
|
||||||
|
}
|
||||||
|
|
||||||
res.status(err.statusCode || 500).json({
|
res.status(err.statusCode || 500).json({
|
||||||
error: err.message || 'Internal Server Error'
|
error: err.message || 'Internal Server Error'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ export * from './createAdmin';
|
|||||||
export * from './hashPassword';
|
export * from './hashPassword';
|
||||||
export * from './signToken';
|
export * from './signToken';
|
||||||
export * from './createAdmin';
|
export * from './createAdmin';
|
||||||
|
export * from './resourceExists';
|
||||||
|
|||||||
17
src/utils/resourceExists.ts
Normal file
17
src/utils/resourceExists.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { NextFunction } from 'express';
|
||||||
|
import { ErrorResponse } from '.';
|
||||||
|
|
||||||
|
export const resourceExists = (
|
||||||
|
resource: any,
|
||||||
|
name: string,
|
||||||
|
id: number,
|
||||||
|
next: NextFunction
|
||||||
|
) => {
|
||||||
|
if (!resource) {
|
||||||
|
return next(
|
||||||
|
new ErrorResponse(404, `${name} with the id of ${id} was not found`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user