UNPKG

vibe-cli-tool

Version:

VibeCLI v1.9 - 模板商店生态,智能部署生成,多语言支持,交互式学习系统,企业级Web全栈应用CLI工具

349 lines (304 loc) 9.75 kB
import { create } from 'zustand' import { persist } from 'zustand/middleware' import { User, AuthTokens } from '@/types/auth' interface AuthState { user: User | null token: string | null refreshToken: string | null isAuthenticated: boolean isLoading: boolean error: string | null // Actions login: (email: string, password: string) => Promise<void> logout: () => Promise<void> register: (data: RegisterData) => Promise<void> refreshTokens: () => Promise<void> updateProfile: (data: Partial<User>) => Promise<void> changePassword: (currentPassword: string, newPassword: string) => Promise<void> resetPassword: (email: string) => Promise<void> confirmPasswordReset: (token: string, newPassword: string) => Promise<void> clearError: () => void setLoading: (loading: boolean) => void } interface RegisterData { email: string password: string name: string terms: boolean } export const useAuthStore = create<AuthState>()( persist( (set, get) => ({ user: null, token: null, refreshToken: null, isAuthenticated: false, isLoading: false, error: null, login: async (email: string, password: string) => { set({ isLoading: true, error: null }) try { const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }) const data = await response.json() if (data.success) { set({ user: data.data.user, token: data.data.tokens.accessToken, refreshToken: data.data.tokens.refreshToken, isAuthenticated: true, isLoading: false, error: null }) } else { throw new Error(data.error || '登录失败') } } catch (error: any) { set({ error: error.message, isLoading: false }) throw error } }, register: async (data: RegisterData) => { set({ isLoading: true, error: null }) try { const response = await fetch('/api/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) const result = await response.json() if (result.success) { set({ user: result.data.user, token: result.data.tokens.accessToken, refreshToken: result.data.tokens.refreshToken, isAuthenticated: true, isLoading: false, error: null }) } else { throw new Error(result.error || '注册失败') } } catch (error: any) { set({ error: error.message, isLoading: false }) throw error } }, logout: async () => { const { token } = get() try { if (token) { await fetch('/api/auth/logout', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }) } } catch (error) { console.error('Logout error:', error) } finally { set({ user: null, token: null, refreshToken: null, isAuthenticated: false, error: null }) } }, refreshTokens: async () => { const { refreshToken } = get() if (!refreshToken) { throw new Error('No refresh token available') } try { const response = await fetch('/api/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken }) }) const data = await response.json() if (data.success) { set({ token: data.data.accessToken, refreshToken: data.data.refreshToken }) } else { throw new Error(data.error || '令牌刷新失败') } } catch (error) { // 刷新失败,清除认证状态 set({ user: null, token: null, refreshToken: null, isAuthenticated: false, error: '会话已过期,请重新登录' }) throw error } }, updateProfile: async (profileData: Partial<User>) => { const { token } = get() set({ isLoading: true, error: null }) try { const response = await fetch('/api/auth/profile', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(profileData) }) const data = await response.json() if (data.success) { set(state => ({ user: state.user ? { ...state.user, ...data.data } : null, isLoading: false })) } else { throw new Error(data.error || '更新资料失败') } } catch (error: any) { set({ error: error.message, isLoading: false }) throw error } }, changePassword: async (currentPassword: string, newPassword: string) => { const { token } = get() set({ isLoading: true, error: null }) try { const response = await fetch('/api/auth/change-password', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ currentPassword, newPassword, confirmPassword: newPassword }) }) const data = await response.json() if (data.success) { set({ isLoading: false }) } else { throw new Error(data.error || '密码修改失败') } } catch (error: any) { set({ error: error.message, isLoading: false }) throw error } }, resetPassword: async (email: string) => { set({ isLoading: true, error: null }) try { const response = await fetch('/api/auth/reset-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }) }) const data = await response.json() if (!data.success) { throw new Error(data.error || '发送重置邮件失败') } set({ isLoading: false }) } catch (error: any) { set({ error: error.message, isLoading: false }) throw error } }, confirmPasswordReset: async (token: string, newPassword: string) => { set({ isLoading: true, error: null }) try { const response = await fetch('/api/auth/reset-password', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token, newPassword, confirmPassword: newPassword }) }) const data = await response.json() if (!data.success) { throw new Error(data.error || '密码重置失败') } set({ isLoading: false }) } catch (error: any) { set({ error: error.message, isLoading: false }) throw error } }, clearError: () => set({ error: null }), setLoading: (loading: boolean) => set({ isLoading: loading }) }), { name: 'auth-storage', partialize: (state) => ({ user: state.user, token: state.token, refreshToken: state.refreshToken, isAuthenticated: state.isAuthenticated }) } ) ) // 自动刷新令牌的拦截器 export const setupAuthInterceptor = () => { const originalFetch = window.fetch window.fetch = async function(input: RequestInfo | URL, init?: RequestInit) { const { token, refreshTokens, logout } = useAuthStore.getState() // 为需要认证的请求添加 Authorization header if (init?.headers && token) { const headers = new Headers(init.headers) if (!headers.has('Authorization')) { headers.set('Authorization', `Bearer ${token}`) init.headers = headers } } const response = await originalFetch(input, init) // 如果返回401,尝试刷新令牌 if (response.status === 401 && token) { try { await refreshTokens() // 重新发送原请求 const { token: newToken } = useAuthStore.getState() if (newToken && init?.headers) { const headers = new Headers(init.headers) headers.set('Authorization', `Bearer ${newToken}`) init.headers = headers return originalFetch(input, init) } } catch (error) { // 刷新失败,登出用户 logout() } } return response } }