125 lines
2.8 KiB
TypeScript
125 lines
2.8 KiB
TypeScript
import { makeAutoObservable } from 'mobx'
|
||
import { useEffect } from 'react'
|
||
|
||
import { signOut as authSignOut, useSession } from '@/lib/auth'
|
||
|
||
interface User {
|
||
id: string
|
||
email: string
|
||
name?: string
|
||
image?: string | null
|
||
createdAt: Date
|
||
updatedAt: Date
|
||
emailVerified?: boolean
|
||
username?: string | null
|
||
banExpires?: Date | null
|
||
banReason?: string | null
|
||
banned?: boolean | null
|
||
displayUsername?: string | null
|
||
metadata?: any | null
|
||
phoneNumber?: string | null
|
||
phoneNumberVerified?: boolean | null
|
||
role?: string | null
|
||
stripeCustomerId?: string | null
|
||
}
|
||
|
||
interface Session {
|
||
user?: User
|
||
session?: {
|
||
id: string
|
||
userId: string
|
||
expiresAt: Date
|
||
createdAt: Date
|
||
updatedAt: Date
|
||
activeOrganizationId?: string | null
|
||
activeTeamId?: string | null
|
||
impersonatedBy?: string | null
|
||
ipAddress?: string | null
|
||
token?: string | null
|
||
userAgent?: string | null
|
||
}
|
||
}
|
||
|
||
class UserStore {
|
||
user: User | null = null
|
||
session: Session | null = null
|
||
isAuthenticated: boolean = false
|
||
isLoading: boolean = false
|
||
error: string | null = null
|
||
|
||
constructor() {
|
||
// autoBind ensures methods keep `this`, and all fields become observable/actions
|
||
makeAutoObservable(this)
|
||
}
|
||
|
||
// 设置用户信息
|
||
setUser(user: User | null) {
|
||
this.user = user
|
||
this.isAuthenticated = !!user
|
||
}
|
||
|
||
// 设置会话信息
|
||
setSession(session: Session | null) {
|
||
this.session = session
|
||
this.user = session?.user || null
|
||
this.isAuthenticated = !!session?.user
|
||
}
|
||
|
||
// 设置加载状态
|
||
setLoading(loading: boolean) {
|
||
this.isLoading = loading
|
||
}
|
||
|
||
// 设置错误信息
|
||
setError(error: string | null) {
|
||
this.error = error
|
||
}
|
||
|
||
// 登出方法
|
||
signOut = async () => {
|
||
this.setLoading(true)
|
||
try {
|
||
await authSignOut()
|
||
this.reset()
|
||
// 登出时重置余额store
|
||
const { userBalanceStore } = await import('./userBalanceStore')
|
||
userBalanceStore.reset()
|
||
} catch (error) {
|
||
this.setError(error instanceof Error ? error.message : '登出失败')
|
||
console.error('登出失败:', error)
|
||
} finally {
|
||
this.setLoading(false)
|
||
}
|
||
}
|
||
|
||
// 从session更新用户状态
|
||
updateFromSession(sessionData: Session | null) {
|
||
this.setSession(sessionData)
|
||
}
|
||
|
||
// 重置store状态
|
||
reset() {
|
||
this.user = null
|
||
this.session = null
|
||
this.isAuthenticated = false
|
||
this.isLoading = false
|
||
this.error = null
|
||
}
|
||
}
|
||
|
||
// 创建单例实例
|
||
export const userStore = new UserStore()
|
||
|
||
// 创建一个hook来使用session数据更新store
|
||
export const useUserSession = () => {
|
||
const session = useSession()
|
||
|
||
// 使用useEffect在副作用中更新store状态,避免在render过程中修改
|
||
useEffect(() => {
|
||
userStore.setLoading(session.isPending)
|
||
userStore.updateFromSession(session.data)
|
||
}, [session.isPending, session.data])
|
||
|
||
return session
|
||
}
|