|
|
@@ -0,0 +1,140 @@
|
|
|
+import React, { useState, useEffect, createContext, useContext } from 'react';
|
|
|
+
|
|
|
+import {
|
|
|
+ useQuery,
|
|
|
+ useQueryClient,
|
|
|
+} from '@tanstack/react-query';
|
|
|
+import axios from 'axios';
|
|
|
+import 'dayjs/locale/zh-cn';
|
|
|
+import type {
|
|
|
+ AuthContextType
|
|
|
+} from '@/share/types';
|
|
|
+import { authClient } from '@/client/api';
|
|
|
+import type { InferResponseType, InferRequestType } from 'hono/client';
|
|
|
+
|
|
|
+type User = InferResponseType<typeof authClient.me.$get, 200>;
|
|
|
+
|
|
|
+
|
|
|
+// 创建认证上下文
|
|
|
+const AuthContext = createContext<AuthContextType<User> | null>(null);
|
|
|
+
|
|
|
+// 认证提供器组件
|
|
|
+export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
|
+ const [user, setUser] = useState<User | null>(null);
|
|
|
+ const [token, setToken] = useState<string | null>(localStorage.getItem('token'));
|
|
|
+ const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
|
|
|
+ const queryClient = useQueryClient();
|
|
|
+
|
|
|
+ // 声明handleLogout函数
|
|
|
+ const handleLogout = async () => {
|
|
|
+ try {
|
|
|
+ // 如果已登录,调用登出API
|
|
|
+ if (token) {
|
|
|
+ await authClient.logout.$post();
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('登出请求失败:', error);
|
|
|
+ } finally {
|
|
|
+ // 清除本地状态
|
|
|
+ setToken(null);
|
|
|
+ setUser(null);
|
|
|
+ setIsAuthenticated(false);
|
|
|
+ localStorage.removeItem('token');
|
|
|
+ // 清除Authorization头
|
|
|
+ delete axios.defaults.headers.common['Authorization'];
|
|
|
+ console.log('登出时已删除全局Authorization头');
|
|
|
+ // 清除所有查询缓存
|
|
|
+ queryClient.clear();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 使用useQuery检查登录状态
|
|
|
+ const { isLoading } = useQuery({
|
|
|
+ queryKey: ['auth', 'status', token],
|
|
|
+ queryFn: async () => {
|
|
|
+ if (!token) {
|
|
|
+ setIsAuthenticated(false);
|
|
|
+ setUser(null);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 设置全局默认请求头
|
|
|
+ axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
|
|
|
+ // 使用API验证当前用户
|
|
|
+ const res = await authClient.me.$get();
|
|
|
+ if (res.status !== 200) {
|
|
|
+ const result = await res.json();
|
|
|
+ throw new Error(result.message)
|
|
|
+ }
|
|
|
+ const currentUser = await res.json();
|
|
|
+ setUser(currentUser);
|
|
|
+ setIsAuthenticated(true);
|
|
|
+ return { isValid: true, user: currentUser };
|
|
|
+ } catch (error) {
|
|
|
+ return { isValid: false };
|
|
|
+ }
|
|
|
+ },
|
|
|
+ enabled: !!token,
|
|
|
+ refetchOnWindowFocus: false,
|
|
|
+ retry: false
|
|
|
+ });
|
|
|
+
|
|
|
+ const handleLogin = async (username: string, password: string, latitude?: number, longitude?: number): Promise<void> => {
|
|
|
+ try {
|
|
|
+ // 使用AuthAPI登录
|
|
|
+ const response = await authClient.login.$post({
|
|
|
+ json: {
|
|
|
+ username,
|
|
|
+ password
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (response.status !== 200) {
|
|
|
+ const result = await response.json()
|
|
|
+ throw new Error(result.message);
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = await response.json()
|
|
|
+
|
|
|
+ // 保存token和用户信息
|
|
|
+ const { token: newToken, user: newUser } = result;
|
|
|
+
|
|
|
+ // 设置全局默认请求头
|
|
|
+ axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`;
|
|
|
+
|
|
|
+ // 保存状态
|
|
|
+ setToken(newToken);
|
|
|
+ setUser(newUser);
|
|
|
+ setIsAuthenticated(true);
|
|
|
+ localStorage.setItem('token', newToken);
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('登录失败:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <AuthContext.Provider
|
|
|
+ value={{
|
|
|
+ user,
|
|
|
+ token,
|
|
|
+ login: handleLogin,
|
|
|
+ logout: handleLogout,
|
|
|
+ isAuthenticated,
|
|
|
+ isLoading
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {children}
|
|
|
+ </AuthContext.Provider>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+// 使用上下文的钩子
|
|
|
+export const useAuth = () => {
|
|
|
+ const context = useContext(AuthContext);
|
|
|
+ if (!context) {
|
|
|
+ throw new Error('useAuth必须在AuthProvider内部使用');
|
|
|
+ }
|
|
|
+ return context;
|
|
|
+};
|