fix topup

This commit is contained in:
songtianlun 2025-08-30 12:48:06 +08:00
parent 57c23e21bb
commit 89549e00ff
8 changed files with 315 additions and 224 deletions

View File

@ -1,20 +1,19 @@
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
import { createServerSupabaseClient } from '@/lib/supabase-server' import { auth } from '@/lib/auth'
import { getCreditStats } from '@/lib/services/credit' import { getCreditStats } from '@/lib/services/credit'
import { headers } from 'next/headers'
export async function GET() { export async function GET() {
try { try {
const supabase = await createServerSupabaseClient() const session = await auth.api.getSession({
const { data: { user }, error: authError } = await supabase.auth.getUser() headers: await headers()
})
if (authError || !user) {
return NextResponse.json( if (!session?.user) {
{ error: 'Unauthorized' }, return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
{ status: 401 }
)
} }
const stats = await getCreditStats(user.id) const stats = await getCreditStats(session.user.id)
return NextResponse.json(stats) return NextResponse.json(stats)
} catch (error) { } catch (error) {

View File

@ -1,7 +1,8 @@
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
import { createServerSupabaseClient } from '@/lib/supabase-server' import { auth } from '@/lib/auth'
import { createOrGetStripeCustomer, createPaymentSession } from '@/lib/stripe' import { createOrGetStripeCustomer, stripe } from '@/lib/stripe'
import { prisma } from '@/lib/prisma' import { prisma } from '@/lib/prisma'
import { headers } from 'next/headers'
export async function POST(request: Request) { export async function POST(request: Request) {
try { try {
@ -22,73 +23,82 @@ export async function POST(request: Request) {
) )
} }
const supabase = await createServerSupabaseClient() // 获取Better Auth会话
const session = await auth.api.getSession({
headers: await headers()
})
// 获取当前用户 if (!session?.user) {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
} }
// 确保用户存在于数据库中,并获取用户信息 const user = session.user
const userData = await prisma.user.upsert({
// 获取或创建用户数据不需要upsert直接查找即可
let userData = await prisma.user.findUnique({
where: { id: user.id }, where: { id: user.id },
update: {
email: user.email || '',
updatedAt: new Date()
},
create: {
id: user.id,
email: user.email || '',
name: user.user_metadata?.full_name || user.email?.split('@')[0] || 'User',
emailVerified: false,
username: user.user_metadata?.username || null,
subscriptionPlanId: 'free',
createdAt: new Date(),
updatedAt: new Date()
},
select: { select: {
email: true, email: true,
username: true name: true
} }
}) })
try { // 如果用户不存在创建用户这种情况应该很少发生因为用户应该已经通过sync API创建了
// 创建或获取 Stripe 客户 if (!userData) {
const customer = await createOrGetStripeCustomer( userData = await prisma.user.create({
user.id, data: {
userData.email, id: user.id,
userData.username || undefined email: user.email,
) name: user.name,
emailVerified: user.emailVerified ?? false,
// 创建支付会话 subscriptionPlanId: 'free'
const session = await createPaymentSession( },
customer.id, select: {
Math.round(amount * 100), // 转换为美分 email: true,
`Credit top-up: $${amount}`, name: true
`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/credits/success?session_id={CHECKOUT_SESSION_ID}`, }
`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/credits/cancel`
)
return NextResponse.json({
success: true,
sessionId: session.id,
sessionUrl: session.url
}) })
} catch (stripeError) {
console.error('Stripe operation failed:', stripeError)
return NextResponse.json(
{ error: 'Failed to create payment session' },
{ status: 500 }
)
} }
// 创建或获取 Stripe 客户
const stripeCustomer = await createOrGetStripeCustomer(
user.id,
userData.email,
userData.name || 'User'
)
// 创建支付会话
const checkoutSession = await stripe.checkout.sessions.create({
customer: stripeCustomer.id,
payment_method_types: ['card'],
line_items: [
{
price_data: {
currency: 'usd',
product_data: {
name: 'Credit Top-up',
description: `Credit top-up for user ${user.id}`,
},
unit_amount: amount * 100, // 转换为美分
},
quantity: 1,
},
],
mode: 'payment',
success_url: `${process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'}/credits/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'}/credits/cancel`,
metadata: {
type: 'credit_topup',
amount: amount.toString(),
userId: user.id // 添加userId到metadata用于验证
},
})
return NextResponse.json({ url: checkoutSession.url })
} catch (error) { } catch (error) {
console.error('Stripe top-up failed:', error) console.error('Stripe top-up failed:', error)
return NextResponse.json( return NextResponse.json(
{ error: 'Failed to process top-up request' }, { error: 'Failed to create payment session' },
{ status: 500 } { status: 500 }
) )
} }

View File

@ -1,19 +1,20 @@
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
import { createServerSupabaseClient } from '@/lib/supabase-server' import { auth } from '@/lib/auth'
import { getCreditTransactions } from '@/lib/services/credit' import { getCreditTransactions } from '@/lib/services/credit'
import { headers } from 'next/headers'
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
try { try {
const supabase = await createServerSupabaseClient() const session = await auth.api.getSession({
const { data: { user }, error: authError } = await supabase.auth.getUser() headers: await headers()
})
if (authError || !user) {
return NextResponse.json( if (!session?.user) {
{ error: 'Unauthorized' }, return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
{ status: 401 }
)
} }
const user = session.user
const { searchParams } = new URL(request.url) const { searchParams } = new URL(request.url)
// Parse query parameters // Parse query parameters

View File

@ -1,8 +1,9 @@
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
import { createServerSupabaseClient } from '@/lib/supabase-server' import { auth } from '@/lib/auth'
import { stripe } from '@/lib/stripe' import { stripe } from '@/lib/stripe'
import { addCredit } from '@/lib/services/credit' import { addCredit } from '@/lib/services/credit'
import { prisma } from '@/lib/prisma' import { prisma } from '@/lib/prisma'
import { headers } from 'next/headers'
export async function GET(request: Request) { export async function GET(request: Request) {
try { try {
@ -16,106 +17,99 @@ export async function GET(request: Request) {
) )
} }
const supabase = await createServerSupabaseClient() // 获取Better Auth会话
const session = await auth.api.getSession({
headers: await headers()
})
// 获取当前用户 if (!session?.user) {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
} }
const user = session.user
try { try {
// 从 Stripe 获取支付会话信息 // 从 Stripe 获取支付会话信息
const session = await stripe.checkout.sessions.retrieve(sessionId) const stripeSession = await stripe.checkout.sessions.retrieve(sessionId)
if (!session) { if (stripeSession.payment_status !== 'paid') {
return NextResponse.json(
{ error: 'Session not found' },
{ status: 404 }
)
}
// 验证会话是否成功支付
if (session.payment_status !== 'paid') {
return NextResponse.json( return NextResponse.json(
{ error: 'Payment not completed' }, { error: 'Payment not completed' },
{ status: 400 } { status: 400 }
) )
} }
// 获取金额 // 验证支付会话属于当前用户
const amount = session.metadata?.amount ? parseFloat(session.metadata.amount) : 0 if (stripeSession.metadata?.userId !== user.id) {
if (amount <= 0) {
return NextResponse.json( return NextResponse.json(
{ error: 'Invalid amount in session' }, { error: 'Payment session does not belong to current user' },
{ status: 400 } { status: 403 }
) )
} }
// 确保用户存在于数据库中 // 获取或创建用户数据
await prisma.user.upsert({ let userData = await prisma.user.findUnique({
where: { id: user.id }, where: { id: user.id }
update: {
email: user.email || '',
updatedAt: new Date()
},
create: {
id: user.id,
email: user.email || '',
name: user.user_metadata?.full_name || user.email?.split('@')[0] || 'User',
emailVerified: false,
username: user.user_metadata?.username || null,
subscriptionPlanId: 'free',
createdAt: new Date(),
updatedAt: new Date()
}
}) })
// 如果用户不存在,创建用户
if (!userData) {
userData = await prisma.user.create({
data: {
id: user.id,
email: user.email,
name: user.name,
emailVerified: user.emailVerified ?? false,
subscriptionPlanId: 'free'
}
})
}
// 检查是否已经处理过这个支付会话 // 检查是否已经处理过这个支付会话
const existingCredit = await prisma.credit.findFirst({ const existingCredit = await prisma.credit.findFirst({
where: { where: {
referenceId: sessionId, referenceId: sessionId,
referenceType: 'stripe_payment' referenceType: 'stripe_session'
} }
}) })
if (existingCredit) { if (existingCredit) {
// 已经处理过,直接返回成功
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
amount, message: 'Payment already processed',
message: 'Payment already processed' amount: existingCredit.amount,
sessionId
}) })
} }
// 添加信用额度 // 从支付会话获取金额Stripe金额以分为单位
const creditTransaction = await addCredit( const amount = (stripeSession.amount_total || 0) / 100
if (amount <= 0) {
return NextResponse.json(
{ error: 'Invalid payment amount' },
{ status: 400 }
)
}
// 添加信用记录
await addCredit(
user.id, user.id,
amount, amount,
'user_purchase', 'user_purchase',
`Stripe payment: $${amount}` `Stripe payment - Session: ${sessionId}`
// 充值的信用不会过期,不设置过期时间
) )
// 更新 credit 记录的 referenceId 和 referenceType
await prisma.credit.update({
where: { id: creditTransaction.id },
data: {
referenceId: sessionId,
referenceType: 'stripe_payment'
}
})
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
message: 'Payment verified and credits added',
amount, amount,
message: `Successfully added $${amount} to your account`, sessionId
transaction: creditTransaction
}) })
} catch (stripeError) { } catch (stripeError) {
console.error('Stripe verification failed:', stripeError) console.error('Stripe error:', stripeError)
return NextResponse.json( return NextResponse.json(
{ error: 'Failed to verify payment with Stripe' }, { error: 'Failed to verify payment with Stripe' },
{ status: 500 } { status: 500 }

View File

@ -1,18 +1,21 @@
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
import { createServerSupabaseClient } from '@/lib/supabase-server' import { auth } from '@/lib/auth'
import { getUserBalance } from '@/lib/services/credit' import { getUserBalance } from '@/lib/services/credit'
import { prisma } from '@/lib/prisma' import { prisma } from '@/lib/prisma'
import { headers } from 'next/headers'
// GET /api/users/credits - 获取用户信用额度概览(向后兼容) // GET /api/users/credits - 获取用户信用额度概览(向后兼容)
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
try { try {
const supabase = await createServerSupabaseClient() const session = await auth.api.getSession({
const { data: { user }, error: authError } = await supabase.auth.getUser() headers: await headers()
})
if (authError || !user) {
if (!session?.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
} }
const user = session.user
const { searchParams } = new URL(request.url) const { searchParams } = new URL(request.url)
const userId = searchParams.get('userId') || user.id const userId = searchParams.get('userId') || user.id

View File

@ -2,7 +2,7 @@
import { useState, useEffect, useCallback } from 'react' import { useState, useEffect, useCallback } from 'react'
import { useTranslations } from 'next-intl' import { useTranslations } from 'next-intl'
import { useAuthUser } from '@/hooks/useAuthUser' import { useBetterAuth } from '@/hooks/useBetterAuth'
import { Header } from '@/components/layout/Header' import { Header } from '@/components/layout/Header'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card' import { Card } from '@/components/ui/card'
@ -51,7 +51,7 @@ interface CreditTransactionsResult {
} }
export default function CreditsPage() { export default function CreditsPage() {
const { user, loading } = useAuthUser() const { user, loading } = useBetterAuth()
const t = useTranslations('credits') const t = useTranslations('credits')
const [transactions, setTransactions] = useState<CreditTransaction[]>([]) const [transactions, setTransactions] = useState<CreditTransaction[]>([])
@ -206,10 +206,11 @@ export default function CreditsPage() {
const data = await response.json() const data = await response.json()
if (response.ok && data.success && data.sessionUrl) { if (response.ok && data.url) {
// 重定向到 Stripe Checkout // 重定向到 Stripe Checkout
window.location.href = data.sessionUrl window.location.href = data.url
} else { } else {
console.error("failed to create payment session: ", data.error)
alert(data.error || 'Failed to create payment session') alert(data.error || 'Failed to create payment session')
} }
} catch (error) { } catch (error) {

View File

@ -2,7 +2,7 @@
import { useState, useEffect, useCallback } from 'react' import { useState, useEffect, useCallback } from 'react'
import { useTranslations } from 'next-intl' import { useTranslations } from 'next-intl'
import { useAuthUser } from '@/hooks/useAuthUser' import { useBetterAuth } from '@/hooks/useBetterAuth'
import { Header } from '@/components/layout/Header' import { Header } from '@/components/layout/Header'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
@ -50,7 +50,7 @@ interface CreditInfo {
} }
export default function ProfilePage() { export default function ProfilePage() {
const { user, loading, triggerProfileUpdate } = useAuthUser() const { user, loading, triggerProfileUpdate } = useBetterAuth()
const t = useTranslations('profile') const t = useTranslations('profile')
const tCommon = useTranslations('common') const tCommon = useTranslations('common')
const tAuth = useTranslations('auth') const tAuth = useTranslations('auth')

View File

@ -1,10 +1,14 @@
'use client' 'use client'
import { useEffect, useState, useCallback, useRef } from 'react' import { useEffect, useState, useCallback } from 'react'
import { useSession } from '@/lib/auth-client' import { useSession } from '@/lib/auth-client'
import { userCache, UserData, SyncTrigger, SyncOptions, shouldSync } from '@/lib/user-cache' import { userCache, UserData, SyncTrigger, SyncOptions, shouldSync } from '@/lib/user-cache'
import type { User } from '@/lib/auth' import type { User } from '@/lib/auth'
// 全局防抖变量
const debounceTimeouts: Map<string, NodeJS.Timeout> = new Map()
const pendingPromises: Map<string, Promise<void>> = new Map()
interface AuthUserState { interface AuthUserState {
user: User | null user: User | null
userData: UserData | null userData: UserData | null
@ -12,20 +16,48 @@ interface AuthUserState {
isAdmin: boolean isAdmin: boolean
} }
// 全局状态管理,避免多个钩子实例重复调用
let globalAuthState: AuthUserState = {
user: null,
userData: null,
loading: true,
isAdmin: false,
}
const stateListeners: Set<(state: AuthUserState) => void> = new Set()
let isGlobalInitialized = false
let currentUserId: string | null = null
let lastSyncTime: number = 0
// 广播状态变化给所有监听器
const notifyStateChange = (newState: AuthUserState) => {
globalAuthState = { ...newState }
stateListeners.forEach(listener => listener(newState))
}
export function useBetterAuth() { export function useBetterAuth() {
const [state, setState] = useState<AuthUserState>({ const [state, setState] = useState<AuthUserState>(globalAuthState)
user: null,
userData: null,
loading: true,
isAdmin: false,
})
const { data: session, isPending } = useSession() const { data: session, isPending } = useSession()
const isInitializedRef = useRef(false)
const currentUserIdRef = useRef<string | null>(null)
// 同步用户数据到数据库 // 注册状态监听器
const syncUserToDatabase = useCallback(async (userId: string, options: SyncOptions) => { useEffect(() => {
const listener = (newState: AuthUserState) => {
setState(newState)
}
stateListeners.add(listener)
return () => {
stateListeners.delete(listener)
}
}, [])
// 全局同步函数,防止重复调用
const globalSyncUserToDatabase = useCallback(async (userId: string, options: SyncOptions) => {
// 检查是否在短时间内已经同步过
const now = Date.now()
if (!options.force && now - lastSyncTime < 5000) { // 5秒内不重复同步
return
}
if (!shouldSync(userId, options)) { if (!shouldSync(userId, options)) {
return return
} }
@ -42,6 +74,7 @@ export function useBetterAuth() {
} }
userCache.recordSync(userId) userCache.recordSync(userId)
lastSyncTime = Date.now()
console.log(`User synced to database: ${userId} (trigger: ${options.trigger})`) console.log(`User synced to database: ${userId} (trigger: ${options.trigger})`)
} catch (error) { } catch (error) {
console.error('Failed to sync user to database:', error) console.error('Failed to sync user to database:', error)
@ -50,17 +83,18 @@ export function useBetterAuth() {
}) })
}, []) }, [])
// 获取用户数据 // 全局获取用户数据函数
const fetchUserData = useCallback(async (userId: string, options: SyncOptions) => { const globalFetchUserData = useCallback(async (userId: string, options: SyncOptions) => {
// 先检查缓存 // 先检查缓存
if (!options.skipCache) { if (!options.skipCache) {
const cachedData = userCache.getUserData(userId) const cachedData = userCache.getUserData(userId)
if (cachedData) { if (cachedData) {
setState(prev => ({ const newState = {
...prev, ...globalAuthState,
userData: cachedData, userData: cachedData,
isAdmin: cachedData.isAdmin || false, isAdmin: cachedData.isAdmin || false,
})) }
notifyStateChange(newState)
return cachedData return cachedData
} }
} }
@ -77,12 +111,13 @@ export function useBetterAuth() {
// 更新缓存 // 更新缓存
userCache.setUserData(userId, userData) userCache.setUserData(userId, userData)
// 更新状态 // 更新全局状态
setState(prev => ({ const newState = {
...prev, ...globalAuthState,
userData, userData,
isAdmin: userData.isAdmin || false, isAdmin: userData.isAdmin || false,
})) }
notifyStateChange(newState)
return userData return userData
} catch (error) { } catch (error) {
@ -91,47 +126,98 @@ export function useBetterAuth() {
} }
}, []) }, [])
// 处理用户状态变化 // 立即更新用户状态(不防抖)
const handleUserChange = useCallback(async ( const updateUserState = useCallback((user: User | null) => {
const newState = {
...globalAuthState,
user,
loading: false,
}
notifyStateChange(newState)
// 如果用户变化了,清除旧用户的缓存
if (currentUserId && currentUserId !== user?.id) {
userCache.clearUserCache(currentUserId)
lastSyncTime = 0 // 重置同步时间
}
currentUserId = user?.id || null
if (!user) {
const userLoggedOutState = {
...globalAuthState,
user: null,
userData: null,
isAdmin: false,
loading: false,
}
notifyStateChange(userLoggedOutState)
}
}, [])
// 全局处理用户数据同步(可以防抖)
const globalHandleUserDataSync = useCallback(async (
user: User | null, user: User | null,
trigger: SyncTrigger = SyncTrigger.INITIAL_LOAD trigger: SyncTrigger = SyncTrigger.INITIAL_LOAD
) => { ) => {
// 更新用户状态 if (!user) return
setState(prev => ({
...prev,
user,
loading: false,
}))
// 如果用户变化了,清除旧用户的缓存 // 避免重复处理相同用户
if (currentUserIdRef.current && currentUserIdRef.current !== user?.id) { if (trigger === SyncTrigger.INITIAL_LOAD && isGlobalInitialized && user.id === currentUserId) {
userCache.clearUserCache(currentUserIdRef.current)
}
currentUserIdRef.current = user?.id || null
if (!user) {
setState(prev => ({
...prev,
userData: null,
isAdmin: false,
}))
return return
} }
try { try {
const syncOptions: SyncOptions = { trigger } const syncOptions: SyncOptions = { trigger }
// 并行执行同步和获取用户数据 // 并行执行同步和获取用户数据,但添加去重机制
await Promise.all([ await Promise.all([
syncUserToDatabase(user.id, syncOptions), globalSyncUserToDatabase(user.id, syncOptions),
fetchUserData(user.id, syncOptions), globalFetchUserData(user.id, syncOptions),
]) ])
console.log(`User data loaded: ${user.id} (trigger: ${trigger})`) console.log(`User data loaded: ${user.id} (trigger: ${trigger})`)
} catch (error) { } catch (error) {
console.error('Failed to handle user change:', error) console.error('Failed to handle user change:', error)
} }
}, [syncUserToDatabase, fetchUserData]) }, [globalSyncUserToDatabase, globalFetchUserData])
// 创建防抖版本的用户数据同步处理
const debouncedUserDataSync = useCallback(async (
user: User | null,
trigger: SyncTrigger = SyncTrigger.INITIAL_LOAD
) => {
const key = `${user?.id || 'anonymous'}-${trigger}`
// 清除之前的超时
const existingTimeout = debounceTimeouts.get(key)
if (existingTimeout) {
clearTimeout(existingTimeout)
}
// 如果有正在执行的Promise直接返回
const existingPromise = pendingPromises.get(key)
if (existingPromise) {
return existingPromise
}
// 创建新的防抖Promise
const debouncePromise = new Promise<void>((resolve) => {
const timeout = setTimeout(async () => {
debounceTimeouts.delete(key)
try {
await globalHandleUserDataSync(user, trigger)
} finally {
pendingPromises.delete(key)
resolve()
}
}, 300)
debounceTimeouts.set(key, timeout)
})
pendingPromises.set(key, debouncePromise)
return debouncePromise
}, [globalHandleUserDataSync])
// 监听 Better Auth 会话变化 // 监听 Better Auth 会话变化
useEffect(() => { useEffect(() => {
@ -141,12 +227,18 @@ export function useBetterAuth() {
if (!mounted) return if (!mounted) return
const user = session?.user || null const user = session?.user || null
await handleUserChange(user, const trigger = !isGlobalInitialized ? SyncTrigger.INITIAL_LOAD : SyncTrigger.SIGN_IN
!isInitializedRef.current ? SyncTrigger.INITIAL_LOAD : SyncTrigger.SIGN_IN
)
if (!isInitializedRef.current) { // 立即更新用户状态(不延迟)
isInitializedRef.current = true updateUserState(user)
// 异步进行数据同步(防抖延迟)
if (user) {
debouncedUserDataSync(user, trigger)
}
if (!isGlobalInitialized) {
isGlobalInitialized = true
} }
} }
@ -157,22 +249,23 @@ export function useBetterAuth() {
return () => { return () => {
mounted = false mounted = false
} }
}, [session, isPending, handleUserChange]) }, [session, isPending, updateUserState, debouncedUserDataSync])
// 设置loading状态 // 设置loading状态
useEffect(() => { useEffect(() => {
setState(prev => ({ const newState = {
...prev, ...globalAuthState,
loading: isPending loading: isPending
})) }
notifyStateChange(newState)
}, [isPending]) }, [isPending])
// 手动刷新用户数据 // 手动刷新用户数据
const refreshUserData = useCallback(async (force = false) => { const refreshUserData = useCallback(async (force = false) => {
if (!state.user) return if (!globalAuthState.user) return
try { try {
setState(prev => ({ ...prev, loading: true })) notifyStateChange({ ...globalAuthState, loading: true })
const syncOptions: SyncOptions = { const syncOptions: SyncOptions = {
trigger: SyncTrigger.MANUAL_REFRESH, trigger: SyncTrigger.MANUAL_REFRESH,
@ -181,15 +274,15 @@ export function useBetterAuth() {
} }
await Promise.all([ await Promise.all([
syncUserToDatabase(state.user.id, syncOptions), globalSyncUserToDatabase(globalAuthState.user.id, syncOptions),
fetchUserData(state.user.id, syncOptions), globalFetchUserData(globalAuthState.user.id, syncOptions),
]) ])
} catch (error) { } catch (error) {
console.error('Failed to refresh user data:', error) console.error('Failed to refresh user data:', error)
} finally { } finally {
setState(prev => ({ ...prev, loading: false })) notifyStateChange({ ...globalAuthState, loading: false })
} }
}, [state.user, syncUserToDatabase, fetchUserData]) }, [globalSyncUserToDatabase, globalFetchUserData])
// 登出 // 登出
const signOut = useCallback(async () => { const signOut = useCallback(async () => {
@ -198,10 +291,15 @@ export function useBetterAuth() {
await betterSignOut() await betterSignOut()
// 清除缓存 // 清除缓存
if (currentUserIdRef.current) { if (currentUserId) {
userCache.clearUserCache(currentUserIdRef.current) userCache.clearUserCache(currentUserId)
} }
// 重置全局状态
isGlobalInitialized = false
currentUserId = null
lastSyncTime = 0
// 重定向到首页 // 重定向到首页
window.location.href = '/' window.location.href = '/'
} catch (error) { } catch (error) {
@ -211,40 +309,25 @@ export function useBetterAuth() {
// 触发用户信息更新(用于个人资料更新后) // 触发用户信息更新(用于个人资料更新后)
const triggerProfileUpdate = useCallback(async () => { const triggerProfileUpdate = useCallback(async () => {
if (!state.user) return if (!globalAuthState.user) return
const syncOptions: SyncOptions = {
trigger: SyncTrigger.PROFILE_UPDATE,
force: true,
skipCache: true,
}
try { try {
await Promise.all([ await globalHandleUserDataSync(globalAuthState.user, SyncTrigger.PROFILE_UPDATE)
syncUserToDatabase(state.user.id, syncOptions),
fetchUserData(state.user.id, syncOptions),
])
} catch (error) { } catch (error) {
console.error('Failed to update profile:', error) console.error('Failed to update profile:', error)
} }
}, [state.user, syncUserToDatabase, fetchUserData]) }, [globalHandleUserDataSync])
// 触发订阅状态更新 // 触发订阅状态更新
const triggerSubscriptionUpdate = useCallback(async () => { const triggerSubscriptionUpdate = useCallback(async () => {
if (!state.user) return if (!globalAuthState.user) return
const syncOptions: SyncOptions = {
trigger: SyncTrigger.SUBSCRIPTION_CHANGE,
force: true,
skipCache: true,
}
try { try {
await fetchUserData(state.user.id, syncOptions) await globalHandleUserDataSync(globalAuthState.user, SyncTrigger.SUBSCRIPTION_CHANGE)
} catch (error) { } catch (error) {
console.error('Failed to update subscription:', error) console.error('Failed to update subscription:', error)
} }
}, [state.user, fetchUserData]) }, [globalHandleUserDataSync])
return { return {
user: state.user, user: state.user,