Task: Add auth context and provider and add routes and route wrapper and more configs and utils
This commit is contained in:
parent
26451a4291
commit
85c81f5e2a
@ -1,3 +1,5 @@
|
||||
export const AppConfig = {
|
||||
APP_NAME: process.env.REACT_APP_NAME
|
||||
APP_NAME: process.env.REACT_APP_NAME,
|
||||
|
||||
CLIENT_TOKEN_KEY: "client_token",
|
||||
}
|
131
src/context/AuthContext.tsx
Normal file
131
src/context/AuthContext.tsx
Normal file
@ -0,0 +1,131 @@
|
||||
import { AppConfig } from '@/config'
|
||||
import { printError, printInfo } from '@/utils/log_util'
|
||||
import { getStorage, setStorage } from '@/utils/ls_util'
|
||||
import React, { useContext, useCallback, useReducer, useEffect } from 'react'
|
||||
|
||||
interface AuthContextState {
|
||||
login: (args: { username: string; password: string }) => Promise<void>
|
||||
state: {
|
||||
user: {
|
||||
username: string
|
||||
userId: string
|
||||
} | null
|
||||
}
|
||||
getToken: () => string | undefined | null
|
||||
logout: () => void
|
||||
isLogin: () => boolean
|
||||
}
|
||||
|
||||
enum AuthActionType {
|
||||
'LOGIN' = 'LOGIN',
|
||||
'LOGOUT' = 'LOGOUT',
|
||||
}
|
||||
|
||||
const AuthReducer: (
|
||||
state: AuthContextState['state'],
|
||||
action: {
|
||||
type: AuthActionType
|
||||
payload?: any
|
||||
}
|
||||
) => AuthContextState['state'] = (state, action) => {
|
||||
switch (action.type) {
|
||||
case AuthActionType.LOGIN:
|
||||
return {
|
||||
...state,
|
||||
user: action.payload,
|
||||
}
|
||||
case AuthActionType.LOGOUT:
|
||||
return {
|
||||
...state,
|
||||
user: null,
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
const AuthContext = React.createContext<AuthContextState>({
|
||||
login: async () => {},
|
||||
state: {
|
||||
user: null,
|
||||
},
|
||||
getToken: () => {
|
||||
return undefined
|
||||
},
|
||||
logout: () => {},
|
||||
isLogin: () => { return false },
|
||||
})
|
||||
|
||||
const AuthProvider: React.FC = (props) => {
|
||||
const [state, dispatch] = useReducer(AuthReducer, {
|
||||
user: null,
|
||||
})
|
||||
|
||||
const login = async (args: { username: string; password: string }) => {
|
||||
const res = {
|
||||
data: {
|
||||
token: 'DEMO_HAH',
|
||||
},
|
||||
}
|
||||
|
||||
setStorage(AppConfig.CLIENT_TOKEN_KEY, res.data.token)
|
||||
await verify()
|
||||
dispatch({
|
||||
type: AuthActionType.LOGIN,
|
||||
payload: args,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const doVerify = async () => {
|
||||
const token = getStorage(AppConfig.CLIENT_TOKEN_KEY)
|
||||
const userAuthInfo = {}
|
||||
|
||||
printInfo("User details =>", token, userAuthInfo)
|
||||
}
|
||||
|
||||
doVerify()
|
||||
}, [])
|
||||
|
||||
const verify = async () => {
|
||||
const token = getStorage(AppConfig.CLIENT_TOKEN_KEY)
|
||||
printError('Verify not implemented with token: ', token)
|
||||
const userAuthInfo = {}
|
||||
return userAuthInfo
|
||||
}
|
||||
|
||||
const logout = useCallback(() => {
|
||||
setStorage(AppConfig.CLIENT_TOKEN_KEY, undefined)
|
||||
dispatch({
|
||||
type: AuthActionType.LOGOUT,
|
||||
})
|
||||
}, [])
|
||||
|
||||
const getToken = () => getStorage(AppConfig.CLIENT_TOKEN_KEY)
|
||||
|
||||
const isLogin = (): boolean => {
|
||||
return getToken() != null
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
value={{
|
||||
login,
|
||||
state,
|
||||
logout,
|
||||
isLogin,
|
||||
getToken,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useAuthContext = () => {
|
||||
return useContext(AuthContext)
|
||||
}
|
||||
|
||||
export default AuthProvider
|
@ -2,8 +2,8 @@ import './index.less'
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<div className="about-title">
|
||||
<h1>About Us</h1>
|
||||
<div>
|
||||
<h1 className="about-title">About Us</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
7
src/pages/Error/403.tsx
Normal file
7
src/pages/Error/403.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export default function AccessDenied() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Access denied!</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
7
src/pages/Error/404.tsx
Normal file
7
src/pages/Error/404.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Not found!</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -2,8 +2,8 @@ import './index.less'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="home-title">
|
||||
<h1>Home</h1>
|
||||
<div>
|
||||
<h1 className="home-title">Home</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
4
src/pages/Login/index.less
Normal file
4
src/pages/Login/index.less
Normal file
@ -0,0 +1,4 @@
|
||||
.login-title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
9
src/pages/Login/index.tsx
Normal file
9
src/pages/Login/index.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import './index.less'
|
||||
|
||||
export default function Login() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="login-title">Login</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
7
src/pages/Profile/index.tsx
Normal file
7
src/pages/Profile/index.tsx
Normal file
@ -0,0 +1,7 @@
|
||||
export default function Profile() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Profile</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
21
src/routes/AuthRoute.tsx
Normal file
21
src/routes/AuthRoute.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { useAuthContext } from '@/context/AuthContext'
|
||||
import AccessDenied from '@/pages/Error/403'
|
||||
import { Route } from 'react-router-dom'
|
||||
|
||||
const AuthRoute = (props: any) => {
|
||||
const { component, ...rest } = props
|
||||
const { isLogin } = useAuthContext()
|
||||
|
||||
if (!isLogin()) {
|
||||
return (<AccessDenied />)
|
||||
}
|
||||
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={component}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthRoute
|
@ -1,4 +1,6 @@
|
||||
import AuthProvider from '@/context/AuthContext'
|
||||
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
|
||||
import AuthRoute from './AuthRoute'
|
||||
import { routes } from './routes'
|
||||
|
||||
const RouterView = () => {
|
||||
@ -6,6 +8,14 @@ const RouterView = () => {
|
||||
<Router>
|
||||
<Switch>
|
||||
{routes.map((route) => {
|
||||
const { withAuthority } = route
|
||||
if (withAuthority) {
|
||||
return (
|
||||
<AuthProvider key={route.key}>
|
||||
<AuthRoute {...route} />
|
||||
</AuthProvider>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Route
|
||||
key={route.key}
|
||||
@ -16,6 +26,7 @@ const RouterView = () => {
|
||||
path={route.path}
|
||||
/>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</Switch>
|
||||
</Router>
|
||||
|
27
src/routes/interfaces.ts
Normal file
27
src/routes/interfaces.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { RouteProps } from 'react-router-dom'
|
||||
|
||||
export interface Authority {
|
||||
authority?: string | undefined
|
||||
strict?: boolean | undefined
|
||||
with?: Authority | undefined
|
||||
}
|
||||
|
||||
export interface AuthorityProps {
|
||||
withAuthority?: boolean | undefined
|
||||
authorities?: Authority | string[] | string | undefined
|
||||
strictAuthority?: boolean | undefined
|
||||
}
|
||||
|
||||
export interface CustomRouteProps extends RouteProps, AuthorityProps {
|
||||
key: string
|
||||
}
|
||||
|
||||
export interface MenuProps {
|
||||
icon: any
|
||||
label?: string | undefined
|
||||
}
|
||||
|
||||
export interface SideSubMenuProps extends MenuProps {
|
||||
key?: string | undefined
|
||||
subMenus: SideSubMenuProps[]
|
||||
}
|
@ -1,36 +1,18 @@
|
||||
import { RouteProps } from 'react-router-dom'
|
||||
import About from '../pages/About'
|
||||
import Home from '../pages/Home'
|
||||
import RouteTypes from './types'
|
||||
|
||||
interface Authority {
|
||||
authority?: string | undefined
|
||||
strict?: boolean | undefined
|
||||
with?: Authority | undefined
|
||||
}
|
||||
|
||||
interface AuthorityProps {
|
||||
authorities?: Authority | string[] | string | undefined
|
||||
strictAuthority?: boolean | undefined
|
||||
}
|
||||
|
||||
interface CustomRouteProps extends RouteProps, AuthorityProps {
|
||||
key: string
|
||||
}
|
||||
|
||||
export interface MenuProps {
|
||||
icon: any
|
||||
label?: string | undefined
|
||||
}
|
||||
|
||||
export interface SideMenuRouteProps extends CustomRouteProps, MenuProps {}
|
||||
|
||||
export interface SideSubMenuProps extends MenuProps {
|
||||
key?: string | undefined
|
||||
subMenus: SideSubMenuProps[]
|
||||
}
|
||||
import About from '@/pages/About'
|
||||
import NotFound from '@/pages/Error/404'
|
||||
import Home from '@/pages/Home'
|
||||
import Login from '@/pages/Login'
|
||||
import Profile from '@/pages/Profile'
|
||||
import { CustomRouteProps } from '@/routes/interfaces'
|
||||
import { RouteTypes } from '@/routes/types'
|
||||
|
||||
const routes: CustomRouteProps[] = [
|
||||
// Auth
|
||||
{
|
||||
key: 'login',
|
||||
path: RouteTypes.LOGIN,
|
||||
component: () => <Login />,
|
||||
},
|
||||
{
|
||||
key: 'home',
|
||||
exact: true,
|
||||
@ -42,8 +24,20 @@ const routes: CustomRouteProps[] = [
|
||||
path: RouteTypes.ABOUT,
|
||||
component: () => <About />,
|
||||
},
|
||||
{
|
||||
exact: true,
|
||||
key: 'profile',
|
||||
path: RouteTypes.PROFILE,
|
||||
component: () => <Profile />,
|
||||
withAuthority: true,
|
||||
},
|
||||
|
||||
// Errors
|
||||
{
|
||||
key: 'notfound',
|
||||
path: RouteTypes.ERROR_404,
|
||||
component: () => <NotFound />,
|
||||
},
|
||||
]
|
||||
|
||||
const menus: (SideMenuRouteProps | SideSubMenuProps)[] = []
|
||||
|
||||
export { routes, menus }
|
||||
export { routes }
|
||||
|
@ -1,6 +1,15 @@
|
||||
const RouteTypes = {
|
||||
HOME: "/",
|
||||
ABOUT: "/about",
|
||||
PROFILE: "/profile",
|
||||
|
||||
// Auth
|
||||
LOGIN: '/login',
|
||||
|
||||
// Errors
|
||||
ERROR_404: "**",
|
||||
}
|
||||
|
||||
export default RouteTypes
|
||||
export {
|
||||
RouteTypes,
|
||||
}
|
3
src/utils/log_util.ts
Normal file
3
src/utils/log_util.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const printInfo = (message?: any, ...opts: any[]) => console.log(message, opts)
|
||||
export const printError = (message?: any, ...opts: any[]) => console.error(message, opts)
|
||||
export const printWarn = (message?: any, ...opts: any[]) => console.warn(message, opts)
|
7
src/utils/ls_util.ts
Normal file
7
src/utils/ls_util.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export const setStorage = (key: string, value?: any) => {
|
||||
localStorage.setItem(key, JSON.stringify(value))
|
||||
}
|
||||
|
||||
export const getStorage = (key: string, defaultValue?: string): (string | undefined | null) => {
|
||||
return localStorage.getItem(key) ?? defaultValue
|
||||
}
|
Loading…
Reference in New Issue
Block a user