fix(web): align auth contract with api
This commit is contained in:
@@ -11,9 +11,13 @@ export interface LoginRequest {
|
||||
|
||||
export interface LoginResponse {
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
refresh_token?: string | null;
|
||||
token_type: string;
|
||||
expires_in: number;
|
||||
user_id: string;
|
||||
email: string;
|
||||
username: string;
|
||||
display_name: string;
|
||||
}
|
||||
|
||||
export interface RegisterRequest {
|
||||
@@ -25,13 +29,42 @@ export interface RegisterRequest {
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
user_id: string;
|
||||
email: string;
|
||||
username: string;
|
||||
display_name: string;
|
||||
is_email_verified: boolean;
|
||||
created_at: string;
|
||||
email_verified: boolean;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
interface UserResponse {
|
||||
id?: string;
|
||||
user_id?: string;
|
||||
email: string;
|
||||
username: string;
|
||||
display_name: string;
|
||||
is_email_verified?: boolean;
|
||||
email_verified?: boolean;
|
||||
created_at?: string;
|
||||
}
|
||||
|
||||
const normalizeUser = (data: UserResponse): User => {
|
||||
const userId = data.id ?? data.user_id ?? '';
|
||||
const emailVerified = data.is_email_verified ?? data.email_verified ?? false;
|
||||
|
||||
return {
|
||||
id: userId,
|
||||
user_id: userId,
|
||||
email: data.email,
|
||||
username: data.username,
|
||||
display_name: data.display_name,
|
||||
is_email_verified: emailVerified,
|
||||
email_verified: emailVerified,
|
||||
created_at: data.created_at,
|
||||
};
|
||||
};
|
||||
|
||||
// 登录
|
||||
export const login = async (data: LoginRequest): Promise<LoginResponse> => {
|
||||
const response = await apiClient.post('/auth/login', data);
|
||||
@@ -51,8 +84,8 @@ export const logout = async (): Promise<void> => {
|
||||
|
||||
// 获取当前用户
|
||||
export const getCurrentUser = async (): Promise<User> => {
|
||||
const response = await apiClient.get('/auth/me');
|
||||
return response.data;
|
||||
const response = await apiClient.get<UserResponse>('/auth/me');
|
||||
return normalizeUser(response.data);
|
||||
};
|
||||
|
||||
// 请求密码重置
|
||||
|
||||
@@ -27,41 +27,13 @@ apiClient.interceptors.request.use(
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器:处理错误和 Token 刷新
|
||||
// 响应拦截器:处理未授权状态
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error: AxiosError) => {
|
||||
const originalRequest = error.config;
|
||||
|
||||
// Token 过期,尝试刷新
|
||||
if (error.response?.status === 401 && originalRequest) {
|
||||
const refreshToken = localStorage.getItem('refresh_token');
|
||||
|
||||
if (refreshToken) {
|
||||
try {
|
||||
const { data } = await axios.post('/api/v1/auth/refresh', {
|
||||
refresh_token: refreshToken,
|
||||
});
|
||||
|
||||
// 保存新 Token
|
||||
localStorage.setItem('access_token', data.access_token);
|
||||
|
||||
// 重试原请求
|
||||
if (originalRequest.headers) {
|
||||
originalRequest.headers.Authorization = `Bearer ${data.access_token}`;
|
||||
}
|
||||
return apiClient(originalRequest);
|
||||
} catch (refreshError) {
|
||||
// 刷新失败,清除 Token 并跳转登录
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('refresh_token');
|
||||
window.location.href = '/login';
|
||||
return Promise.reject(refreshError);
|
||||
}
|
||||
} else {
|
||||
// 没有 refresh token,直接跳转登录
|
||||
window.location.href = '/login';
|
||||
}
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('refresh_token');
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
|
||||
@@ -18,13 +18,19 @@ export const useLogin = () => {
|
||||
// 手动处理成功后的逻辑
|
||||
const login = async (credentials: Parameters<typeof authApi.login>[0]) => {
|
||||
const data = await mutation.mutateAsync(credentials);
|
||||
const refreshToken = data.refresh_token ?? null;
|
||||
|
||||
// 先保存 token
|
||||
localStorage.setItem('access_token', data.access_token);
|
||||
localStorage.setItem('refresh_token', data.refresh_token);
|
||||
if (refreshToken) {
|
||||
localStorage.setItem('refresh_token', refreshToken);
|
||||
} else {
|
||||
localStorage.removeItem('refresh_token');
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
const user = await authApi.getCurrentUser();
|
||||
setAuth(user, data.access_token, data.refresh_token);
|
||||
setAuth(user, data.access_token, refreshToken);
|
||||
navigate('/workspaces');
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -7,10 +7,12 @@ import { persist } from 'zustand/middleware';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
user_id: string;
|
||||
email: string;
|
||||
username: string;
|
||||
display_name: string;
|
||||
is_email_verified: boolean;
|
||||
email_verified: boolean;
|
||||
}
|
||||
|
||||
interface AuthState {
|
||||
@@ -20,7 +22,8 @@ interface AuthState {
|
||||
isAuthenticated: boolean;
|
||||
|
||||
// Actions
|
||||
setAuth: (user: User, accessToken: string, refreshToken: string) => void;
|
||||
setAuth: (user: User, accessToken: string, refreshToken?: string | null) => void;
|
||||
|
||||
clearAuth: () => void;
|
||||
setUser: (user: User) => void;
|
||||
}
|
||||
@@ -33,9 +36,13 @@ export const useAuthStore = create<AuthState>()(
|
||||
refreshToken: null,
|
||||
isAuthenticated: false,
|
||||
|
||||
setAuth: (user, accessToken, refreshToken) => {
|
||||
setAuth: (user, accessToken, refreshToken = null) => {
|
||||
localStorage.setItem('access_token', accessToken);
|
||||
localStorage.setItem('refresh_token', refreshToken);
|
||||
if (refreshToken) {
|
||||
localStorage.setItem('refresh_token', refreshToken);
|
||||
} else {
|
||||
localStorage.removeItem('refresh_token');
|
||||
}
|
||||
set({
|
||||
user,
|
||||
accessToken,
|
||||
|
||||
Reference in New Issue
Block a user