vibe-cli-tool
Version:
VibeCLI v1.9 - 模板商店生态,智能部署生成,多语言支持,交互式学习系统,企业级Web全栈应用CLI工具
349 lines (304 loc) • 9.75 kB
text/typescript
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
}
}