147 lines
5.4 KiB
TypeScript
147 lines
5.4 KiB
TypeScript
import { clearCredentials, selectCurrentToken, selectCurrentUser, setCredentials } from '@/features/auth/authSlice';
|
||
import { EVENT_TYPES, eventEmitter } from '@/lib/event-util';
|
||
import { fetchApi, refreshAuthToken } from '@/lib/server-api-util';
|
||
import { store } from '@/store';
|
||
import { User } from '@/types/user';
|
||
import * as SecureStore from 'expo-secure-store';
|
||
import React, { createContext, ReactNode, useContext, useEffect } from 'react';
|
||
import { Platform } from 'react-native';
|
||
import { useDispatch, useSelector } from 'react-redux';
|
||
|
||
interface AuthContextType {
|
||
user: User | null;
|
||
login: (user: User, jwt: string) => void;
|
||
logout: () => void;
|
||
jwt: string | null;
|
||
isGuester: () => Promise<boolean>;
|
||
isFormalUser: (userMessage: User | null) => boolean;
|
||
}
|
||
|
||
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
||
|
||
export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||
const dispatch = useDispatch();
|
||
const user = useSelector(selectCurrentUser);
|
||
const jwt = useSelector(selectCurrentToken);
|
||
|
||
useEffect(() => {
|
||
// 检查 Redux store 中是否已有 token
|
||
const refreshTokenAction = async () => {
|
||
let token = store.getState().auth.token;
|
||
if (Platform.OS === 'web') {
|
||
token = localStorage.getItem('token') || "";
|
||
} else {
|
||
const storedToken = await SecureStore.getItemAsync('token');
|
||
token = storedToken || "";
|
||
}
|
||
if (token) {
|
||
// 验证当前 token 是否有效
|
||
fetchApi('/user/identity-check', {}, false)
|
||
.catch(async (error) => {
|
||
// console.error("JWT validation failed, attempting to refresh token...");
|
||
try {
|
||
// 尝试刷新 token
|
||
await refreshAuthToken("Token expired");
|
||
// console.log("Token refreshed successfully");
|
||
// Token 刷新成功,不需要做其他操作
|
||
} catch (refreshError) {
|
||
// 刷新 token 失败,才进行登出操作
|
||
// console.error("Token refresh failed, logging out", refreshError);
|
||
logout();
|
||
}
|
||
});
|
||
|
||
} else {
|
||
try {
|
||
// 尝试刷新 token
|
||
await refreshAuthToken("Token expired");
|
||
// console.log("Token refreshed successfully");
|
||
// Token 刷新成功,不需要做其他操作
|
||
} catch (refreshError) {
|
||
// 刷新 token 失败,才进行登出操作
|
||
// console.error("Token refresh failed, logging out", refreshError);
|
||
// 只有在确实需要登出时才调用logout()
|
||
// 如果是首次启动应用,没有token是正常的,不需要登出
|
||
}
|
||
}
|
||
}
|
||
|
||
refreshTokenAction();
|
||
}, [dispatch]);
|
||
|
||
const login = (newUser: User, newJwt: string) => {
|
||
// 更新 Redux store
|
||
dispatch(setCredentials({
|
||
user: newUser,
|
||
token: newJwt
|
||
}));
|
||
|
||
// 判断运行环境是web则存在localstorage或者expo-secure-store中
|
||
if (Platform.OS === 'web') {
|
||
localStorage.setItem('user', JSON.stringify(newUser));
|
||
localStorage.setItem('token', newJwt);
|
||
} else {
|
||
SecureStore.setItemAsync('user', JSON.stringify(newUser));
|
||
SecureStore.setItemAsync('token', newJwt);
|
||
}
|
||
|
||
// 触发事件通知
|
||
eventEmitter.emit(EVENT_TYPES.USER_INFO_UPDATED, newUser);
|
||
};
|
||
|
||
const logout = () => {
|
||
// 清除 Redux store 中的认证信息
|
||
dispatch(clearCredentials());
|
||
if (Platform.OS === 'web') {
|
||
localStorage.setItem('user', "");
|
||
localStorage.setItem('token', "");
|
||
} else {
|
||
SecureStore.setItemAsync('user', "");
|
||
SecureStore.setItemAsync('token', "");
|
||
}
|
||
// 触发事件通知
|
||
eventEmitter.emit(EVENT_TYPES.USER_INFO_UPDATED, null);
|
||
|
||
};
|
||
|
||
// 判断是否是游客身份
|
||
const isGuester = async () => {
|
||
// 调用接口判断是否是游客
|
||
try {
|
||
await fetchApi(`/iam/identity-check`, {
|
||
method: 'POST',
|
||
}, false);
|
||
user?.email === ""
|
||
return false
|
||
} catch (error) {
|
||
return true;
|
||
}
|
||
}
|
||
// 判断是否是正式用户 手机号注册和email注册都为正式用户
|
||
const isFormalUser = (userMessage: User | null) => {
|
||
if (userMessage?.account) {
|
||
if (userMessage?.account?.includes("@")) {
|
||
return true
|
||
} else if (/^1[3-9]\d{9}$/.test(userMessage?.account)) {
|
||
return true
|
||
} else {
|
||
return false
|
||
}
|
||
}
|
||
// 没有用户信息
|
||
return false
|
||
}
|
||
return (
|
||
<AuthContext.Provider value={{ user, login, logout, jwt, isGuester, isFormalUser }}>
|
||
{children}
|
||
</AuthContext.Provider>
|
||
);
|
||
};
|
||
|
||
export const useAuth = () => {
|
||
const context = useContext(AuthContext);
|
||
if (context === undefined) {
|
||
throw new Error('useAuth must be used within an AuthProvider');
|
||
}
|
||
return context;
|
||
}; |