File size: 3,740 Bytes
212c959 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | import { useEffect } from 'react'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { api } from '../utils/api'
import { useAppStore } from '../store/useAppStore'
export function useAuth() {
const queryClient = useQueryClient()
const token = useAppStore((state) => state.token)
const setToken = useAppStore((state) => state.setToken)
const openAuth = useAppStore((state) => state.openAuth)
const closeAuth = useAppStore((state) => state.closeAuth)
const addToast = useAppStore((state) => state.addToast)
const authMode = useAppStore((state) => state.authMode)
const activeModal = useAppStore((state) => state.activeModal)
const startNewChat = useAppStore((state) => state.startNewChat)
useEffect(() => {
if (typeof window === 'undefined') return
const params = new URLSearchParams(window.location.search)
const accessToken = params.get('access_token')
if (accessToken) {
setToken(accessToken)
window.history.replaceState({}, '', window.location.pathname)
}
}, [setToken])
const meQuery = useQuery({
queryKey: ['auth', 'me', token],
queryFn: () => api.get('/auth/me', { token }),
enabled: Boolean(token),
retry: false,
staleTime: 60_000,
})
useEffect(() => {
if (!token || !meQuery.isError) return
setToken(null)
}, [meQuery.isError, setToken, token])
const loginMutation = useMutation({
mutationFn: ({ username, password }) => api.post('/auth/login', { username, password }),
onSuccess: (data) => {
setToken(data.access_token)
closeAuth()
addToast({
title: 'Welcome back',
description: 'You are signed in and ready to chat.',
variant: 'success',
})
queryClient.invalidateQueries({ queryKey: ['auth', 'me'] })
},
onError: (error) => {
addToast({
title: 'Sign in failed',
description: error.message,
variant: 'danger',
})
},
})
const registerMutation = useMutation({
mutationFn: async ({ username, name, password }) => {
await api.post('/auth/register', { username, name, password })
return api.post('/auth/login', { username, password })
},
onSuccess: (data) => {
setToken(data.access_token)
closeAuth()
addToast({
title: 'Account created',
description: 'Your workspace is ready.',
variant: 'success',
})
queryClient.invalidateQueries({ queryKey: ['auth', 'me'] })
},
onError: (error) => {
addToast({
title: 'Registration failed',
description: error.message,
variant: 'danger',
})
},
})
const logout = () => {
setToken(null)
startNewChat()
queryClient.removeQueries({ queryKey: ['auth'] })
queryClient.removeQueries({ queryKey: ['history'] })
queryClient.removeQueries({ queryKey: ['sessions'] })
addToast({
title: 'Signed out',
description: 'Your local session has been cleared.',
variant: 'info',
})
}
const loginWithGoogle = () => {
window.location.href = '/auth/google/login'
}
const requireAuth = (callback) => {
if (!meQuery.data) {
openAuth('login')
return false
}
callback?.()
return true
}
return {
token,
user: meQuery.data || null,
isAuthLoading: Boolean(token) && meQuery.isLoading,
isAuthenticated: Boolean(meQuery.data),
authMode,
isAuthDialogOpen: activeModal === 'auth',
openAuth,
closeAuth,
login: loginMutation.mutateAsync,
register: registerMutation.mutateAsync,
loginPending: loginMutation.isPending,
registerPending: registerMutation.isPending,
logout,
loginWithGoogle,
requireAuth,
}
}
|