import { useEffect } from 'react';
import { useMutation } from '@tanstack/react-query';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { apiClient, setAuthStoreGetter } from '../client';
import { logger } from '@/lib/logger';
* 用户登录请求
*/
export interface UserLoginRequest {
username: string;
password: string;
expire: number;
}
* 用户登录响应
*/
export interface UserLoginResponse {
token: string;
expire_at: string;
}
* 修改密码请求
*/
export interface ChangePasswordRequest {
old_password: string;
new_password: string;
}
* 修改用户名请求
*/
export interface ChangeUsernameRequest {
new_username: string;
}
* 认证状态 Store
*/
interface AuthState {
isAuthenticated: boolean;
isLoading: boolean;
isAPIKeyAuth: boolean;
token: string | null;
expireAt: string | null;
setAuth: (token: string, expireAt: string) => void;
setAPIKeyAuth: (apiKey: string) => void;
checkAuth: () => Promise<void>;
logout: () => void;
}
* 认证状态管理 Store(使用 zustand + persist)
*/
export const useAuthStore = create<AuthState>()(
persist(
(set, get) => ({
isAuthenticated: false,
isLoading: true,
isAPIKeyAuth: false,
token: null,
expireAt: null,
setAuth: (token: string, expireAt: string) => {
set({
isAuthenticated: true,
isAPIKeyAuth: false,
token,
expireAt,
isLoading: false
});
},
setAPIKeyAuth: (apiKey: string) => {
set({
isAuthenticated: true,
isAPIKeyAuth: true,
token: apiKey,
expireAt: null,
isLoading: false
});
},
checkAuth: async () => {
const { token, expireAt, isAPIKeyAuth } = get();
if (!token) {
set({ isAuthenticated: false, isLoading: false });
return;
}
if (!isAPIKeyAuth) {
if (!expireAt || Date.now() >= new Date(expireAt).getTime()) {
get().logout();
return;
}
}
try {
const endpoint = isAPIKeyAuth ? '/api/v1/apikey/login' : '/api/v1/user/status';
await apiClient.get<unknown>(endpoint);
set({ isAuthenticated: true, isLoading: false });
} catch (error) {
logger.error('认证验证失败:', error);
get().logout();
}
},
logout: () => {
set({
isAuthenticated: false,
isAPIKeyAuth: false,
token: null,
expireAt: null,
isLoading: false
});
}
}),
{
name: 'auth-storage',
partialize: (state) => ({
token: state.token,
expireAt: state.expireAt,
isAPIKeyAuth: state.isAPIKeyAuth,
})
}
)
);
if (typeof window !== 'undefined') {
setAuthStoreGetter(() => {
const state = useAuthStore.getState();
return {
token: state.token,
logout: state.logout
};
});
}
* 用户登录 Hook
*
* @example
* const login = useLogin();
* login.mutate({ username: 'admin', password: '123456', expire: 86400 });
*
* if (login.isPending) return <Loading />;
* if (login.isError) return <Error message={login.error.message} />;
*/
export function useLogin() {
const { setAuth } = useAuthStore();
return useMutation({
mutationFn: async (data: UserLoginRequest) => {
return apiClient.post<UserLoginResponse>('/api/v1/user/login', data);
},
onSuccess: (data) => {
setAuth(data.token, data.expire_at);
},
onError: (error) => {
logger.error('登录失败:', error);
},
});
}
* 修改密码 Hook
*
* @example
* const changePassword = useChangePassword();
* changePassword.mutate({ oldPassword: '123', newPassword: '456' });
*/
export function useChangePassword() {
return useMutation({
mutationFn: async (data: { oldPassword: string; newPassword: string }) => {
const payload: ChangePasswordRequest = {
old_password: data.oldPassword,
new_password: data.newPassword,
};
return apiClient.post<string>('/api/v1/user/change-password', payload);
},
onSuccess: (message) => {
logger.log('密码修改成功:', message);
},
onError: (error) => {
logger.error('密码修改失败:', error);
},
});
}
* 修改用户名 Hook
*
* @example
* const changeUsername = useChangeUsername();
* changeUsername.mutate({ newUsername: 'newname' });
*/
export function useChangeUsername() {
return useMutation({
mutationFn: async (data: { newUsername: string }) => {
const payload: ChangeUsernameRequest = {
new_username: data.newUsername,
};
return apiClient.post<string>('/api/v1/user/change-username', payload);
},
onSuccess: (message) => {
logger.log('用户名修改成功:', message);
},
onError: (error) => {
logger.error('用户名修改失败:', error);
},
});
}
* 认证状态和方法 Hook
*
* @example
* const auth = useAuth();
*
* if (auth.isAuthenticated) {
* // 已登录
* }
*
* auth.logout(); // 登出
*/
export function useAuth() {
const store = useAuthStore();
const { checkAuth, isLoading } = store;
useEffect(() => {
if (isLoading) {
checkAuth();
}
}, []);
return {
isAuthenticated: store.isAuthenticated,
isAPIKeyAuth: store.isAPIKeyAuth,
isLoading: store.isLoading,
logout: store.logout,
};
}