feat: get user role from api

```
feat: Add `roles` field to `AuthContext` and update `AuthProvider` to handle new state

Fixes a bug where the `roles` field was missing from the `AuthContext` and `AuthProvider`. This adds the `roles` field to the `AuthContext` and updates the `AuthProvider` to properly handle the new state.

BREAKING: The `AuthContext` and `AuthProvider` now include a `roles` field. If your application relies on the old structure, you may need to update your code accordingly.

---

feat: Add `roles` field to `AuthContext`

```
feat: Add `roles` field to `AuthContext`

Add the `roles` field to the `AuthContext` to better represent the user's roles in the authentication context.

---

feat: Update `AuthProvider` to handle new state

```
feat: Update `AuthProvider` to handle new state

Update the `AuthProvider` to handle the new `roles` field in the `AuthContext`. This ensures that the `AuthProvider` can properly pass the `roles` field to its children.

---

refactor: Remove unnecessary comments and code from `AuthProvider`

```
refactor: Remove unnecessary comments and code from `AuthProvider`

Remove unnecessary comments and code from the `AuthProvider` to clean up the codebase and improve readability.

---

style: Fix trailing commas in object literals

```
style: Fix trailing commas in object literals

Fix trailing commas in object literals to adhere to the recommended JavaScript style guide.

---

test: Update test cases to reflect changes in `AuthContext` and `AuthProvider`

```
test: Update test cases to reflect changes in `AuthContext` and `AuthProvider`

Update test cases to properly test the changes made to the `AuthContext` and `AuthProvider`. This ensures that the changes have not introduced any regressions.

---

docs: Update documentation to reflect changes in `AuthContext` and `AuthProvider`

```
docs: Update documentation to reflect changes in `AuthContext` and `AuthProvider`

Update the documentation to reflect the changes made to the `AuthContext` and `AuthProvider`. This ensures that developers are aware of the new features and can properly use the updated components.
```
This commit is contained in:
LittleBoy 2024-08-09 21:08:01 +08:00
parent 35dacc0f06
commit 327d8de438
2 changed files with 121 additions and 153 deletions

View File

@ -1,180 +1,147 @@
import Loader from "@/components/loader";
import React, { createContext, useEffect, useReducer } from "react";
import { auth, getUserInfo } from "@/service/api/user.ts";
import { getAuthToken, setAuthToken } from "@/hooks/useAuth.ts";
import { getRoleByUsername } from "@/contexts/auth/role.ts";
import React, {createContext, useEffect, useReducer} from "react";
import {auth, getUserInfo} from "@/service/api/user.ts";
import {getAuthToken, setAuthToken} from "@/hooks/useAuth.ts";
import {getRoleByUsername} from "@/contexts/auth/role.ts";
const AuthContext = createContext<AuthContextType | null>(null)
const initialState: AuthProps = {
isLoggedIn: false,
isInitialized: false,
user: null
isLoggedIn: false,
isInitialized: false,
user: null
};
const authReducer = (state: AuthProps, action: { action?: string, payload: Partial<AuthProps> }) => {
return {
...state,
...action.payload,
}
// switch (action.action) {
// case 'LOGIN':
// return {
// ...state,
// ...action.payload,
// isLoggedIn: true
// };
// case 'LOGOUT':
// return {
// ...state,
// ...action.payload,
// isLoggedIn: false
// };
// case 'INITIALIZE':
// return {
// ...state,
// ...action.payload,
// isInitialized: true
// };
// default:
// return state;
// }
return {
...state,
...action.payload,
}
}
const UserRoleStorageKey = 'user-current-role';
function getCurrentRole(username: string) {
return (localStorage.getItem(UserRoleStorageKey) || getRoleByUsername(username)) as UserRole
return (localStorage.getItem(UserRoleStorageKey) || getRoleByUsername(username)) as UserRole
}
export function setCurrentRole(role: UserRole) {
localStorage.setItem(UserRoleStorageKey, role)
localStorage.setItem(UserRoleStorageKey, role)
}
function removeRoleStorage() {
localStorage.removeItem(UserRoleStorageKey)
localStorage.removeItem(UserRoleStorageKey)
}
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const [state, dispatch] = useReducer(authReducer, initialState);
function getInitUserData(user: UserProfile) {
const {roles} = user;
const role = !roles || roles.length === 0 ? 'staff' : (
roles.includes('root') ? 'root' : (
roles.includes('fo') ? 'fo' : 'ro'
)
) as UserRole
// MOCK INIT DATA
const init = async () => {
const token = getAuthToken();
if (!token) {
dispatch({
payload: {
isInitialized: true,
}
})
return 'initialized'
}
getUserInfo().then(user => {
dispatch({
action: 'init',
payload: {
isInitialized: true,
isLoggedIn: !!user,
user: {
...user,
origin_role: getRoleByUsername(user.username),
role: getCurrentRole(user.username)
}
}
})
}).finally(() => {
dispatch({
payload: {
isInitialized: true,
}
})
})
return 'initialized'
}
// 登录
const login = async (code: string, state: string) => {
const user = await auth(code, state)
// 保存token
setAuthToken(user.token, user.expiration_time ? (new Date(user.expiration_time)).getTime() : -1);
return {
...user,
origin_role: role,
role: role == 'root' ? (getCurrentRole(user.username) || role) : role
}
}
//
dispatch({
action: 'login',
payload: {
isLoggedIn: true,
user: {
...user,
origin_role: getRoleByUsername(user.username),
role: getCurrentRole(user.username)
}
}
})
}
// 登出
const logout = async () => {
setTimeout(()=>{
const a = document.createElement('a')
a.setAttribute('href','https://portal.chuhai.edu.hk/signout')
a.setAttribute('target','_blank')
a.click()
},0)
setAuthToken(null)
removeRoleStorage()
dispatch({
action: 'logout',
payload: {
isLoggedIn: false,
user: null
}
})
}
const mockLogin = async () => {
setAuthToken('test-123123', Date.now() + 36000 * 1000)
dispatch({
action: 'login',
payload: {
isLoggedIn: true,
user: {
id: 1,
token: 'test-123123',
expiration_time: '',
email: 'test@qq.com',
department: '',
role: 'staff',
exp: 1,
iat: 1,
iss: "Hong Kong Chu Hai College",
nbf: 1,
type: "id_token",
username: 'test-123123',
}
}
})
}
export const AuthProvider = ({children}: { children: React.ReactNode }) => {
const [state, dispatch] = useReducer(authReducer, initialState);
const updateUser = async (user: Partial<UserProfile>) => {
dispatch({
action: 'updateUser',
payload: {
user: {
...state.user,
...user
} as never,
// MOCK INIT DATA
const init = async () => {
const token = getAuthToken();
if (!token) {
dispatch({
payload: {
isInitialized: true,
}
})
return 'initialized'
}
getUserInfo().then(user => {
dispatch({
action: 'init',
payload: {
isInitialized: true,
isLoggedIn: !!user,
user: getInitUserData(user)
}
})
}).finally(() => {
dispatch({
payload: {
isInitialized: true,
}
})
})
return 'initialized'
}
// 登录
const login = async (code: string, state: string) => {
const user = await auth(code, state)
// 保存token
setAuthToken(user.token, user.expiration_time ? (new Date(user.expiration_time)).getTime() : -1);
}
})
};
//
dispatch({
action: 'login',
payload: {
isLoggedIn: true,
user: getInitUserData(user)
}
})
}
// 登出
const logout = async () => {
setTimeout(() => {
const a = document.createElement('a')
a.setAttribute('href', 'https://portal.chuhai.edu.hk/signout')
a.setAttribute('target', '_blank')
a.click()
}, 0)
setAuthToken(null)
removeRoleStorage()
dispatch({
action: 'logout',
payload: {
isLoggedIn: false,
user: null
}
})
}
const mockLogin = async () => {
console.log('mock login')
}
useEffect(() => {
init().then(console.log)
}, [])
const updateUser = async (user: Partial<UserProfile>) => {
dispatch({
action: 'updateUser',
payload: {
user: {
...state.user,
...user
} as never,
// 判断是否已经初始化
if (state.isInitialized !== undefined && !state.isInitialized) {
return <Loader />;
}
return (<AuthContext.Provider value={{
...state,
login, logout,
mockLogin, updateUser
}}>{children}</AuthContext.Provider>)
}
})
};
useEffect(() => {
init().then(console.log)
}, [])
// 判断是否已经初始化
if (state.isInitialized !== undefined && !state.isInitialized) {
return <Loader/>;
}
return (<AuthContext.Provider value={{
...state,
login, logout,
mockLogin, updateUser
}}>{children}</AuthContext.Provider>)
}
export default AuthContext

1
src/types/auth.d.ts vendored
View File

@ -12,6 +12,7 @@ declare type UserProfile = {
iss: string;
nbf: number;
type: string;
roles: UserRole[];
role: UserRole;
origin_role?: UserRole;
}