Files
doc_ai_frontend/src/contexts/AuthContext.tsx
2025-12-22 17:37:41 +08:00

131 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createContext, useContext, useEffect, useState, ReactNode, useCallback } from 'react';
import { authService } from '../lib/authService';
import { ApiErrorMessages } from '../types/api';
import type { UserInfo } from '../types/api';
interface AuthContextType {
user: UserInfo | null;
token: string | null;
loading: boolean;
initializing: boolean; // 新增初始化状态
signIn: (email: string, password: string) => Promise<{ error: Error | null }>;
signUp: (email: string, password: string) => Promise<{ error: Error | null }>;
signOut: () => Promise<void>;
isAuthenticated: boolean;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: ReactNode }) {
// 直接在 useState 初始化函数中同步恢复会话
const [user, setUser] = useState<UserInfo | null>(() => {
try {
const session = authService.restoreSession();
return session ? session.user : null;
} catch {
return null;
}
});
const [token, setToken] = useState<string | null>(() => {
try {
const session = authService.restoreSession();
return session ? session.token : null;
} catch {
return null;
}
});
const [loading, setLoading] = useState(false);
const [initializing, setInitializing] = useState(false); // 不需要初始化过程了,因为是同步的
// 不再需要 useEffect 里的 restoreSession
/**
* 从错误对象中提取用户友好的错误消息
*/
const getErrorMessage = (error: unknown, fallback: string): string => {
// 检查是否是 ApiError通过 code 属性判断,避免 instanceof 在热更新时失效)
if (error && typeof error === 'object' && 'code' in error) {
const apiError = error as { code: number; message: string };
return ApiErrorMessages[apiError.code] || apiError.message || fallback;
}
if (error instanceof Error) {
return error.message;
}
return fallback;
};
/**
* 登录
*/
const signIn = useCallback(async (email: string, password: string) => {
setLoading(true);
try {
const result = await authService.login({ email, password });
setUser(result.user);
setToken(result.token);
return { error: null };
} catch (error) {
const message = getErrorMessage(error, '登录失败');
return { error: new Error(message) };
} finally {
setLoading(false);
}
}, []);
/**
* 注册
*/
const signUp = useCallback(async (email: string, password: string) => {
setLoading(true);
try {
const result = await authService.register({ email, password });
setUser(result.user);
setToken(result.token);
return { error: null };
} catch (error) {
const message = getErrorMessage(error, '注册失败');
return { error: new Error(message) };
} finally {
setLoading(false);
}
}, []);
/**
* 登出
*/
const signOut = useCallback(async () => {
setLoading(true);
try {
authService.logout();
setUser(null);
setToken(null);
} finally {
setLoading(false);
}
}, []);
const value: AuthContextType = {
user,
token,
loading,
initializing,
signIn,
signUp,
signOut,
isAuthenticated: !!user && !!token,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth() {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}