Добавлены новые зависимости, обновлены стили и улучшена структура проекта. Обновлен README с описанием функционала и технологий. Реализована анимация и адаптивный дизайн. Настроена авторизация с использованием Apollo Client.
This commit is contained in:
367
src/hooks/useAuth.ts
Normal file
367
src/hooks/useAuth.ts
Normal file
@ -0,0 +1,367 @@
|
||||
import { useMutation } from '@apollo/client'
|
||||
import { useState, useEffect } from 'react'
|
||||
import {
|
||||
SEND_SMS_CODE,
|
||||
VERIFY_SMS_CODE,
|
||||
REGISTER_FULFILLMENT_ORGANIZATION,
|
||||
REGISTER_SELLER_ORGANIZATION
|
||||
} from '@/graphql/mutations'
|
||||
import { GET_ME } from '@/graphql/queries'
|
||||
import { setAuthToken, setUserData, removeAuthToken, getAuthToken, apolloClient } from '@/lib/apollo-client'
|
||||
import { useApolloRefresh } from './useApolloRefresh'
|
||||
|
||||
interface User {
|
||||
id: string
|
||||
phone: string
|
||||
avatar?: string
|
||||
createdAt?: string
|
||||
organization?: {
|
||||
id: string
|
||||
inn: string
|
||||
kpp?: string
|
||||
name?: string
|
||||
fullName?: string
|
||||
address?: string
|
||||
addressFull?: string
|
||||
ogrn?: string
|
||||
ogrnDate?: string
|
||||
status?: string
|
||||
actualityDate?: string
|
||||
registrationDate?: string
|
||||
liquidationDate?: string
|
||||
managementName?: string
|
||||
managementPost?: string
|
||||
opfCode?: string
|
||||
opfFull?: string
|
||||
opfShort?: string
|
||||
okato?: string
|
||||
oktmo?: string
|
||||
okpo?: string
|
||||
okved?: string
|
||||
employeeCount?: number
|
||||
revenue?: string
|
||||
taxSystem?: string
|
||||
phones?: unknown
|
||||
emails?: unknown
|
||||
type: 'FULFILLMENT' | 'SELLER' | 'LOGIST' | 'WHOLESALE'
|
||||
apiKeys: Array<{
|
||||
id: string
|
||||
marketplace: 'WILDBERRIES' | 'OZON'
|
||||
isActive: boolean
|
||||
validationData?: unknown
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
interface UseAuthReturn {
|
||||
// SMS методы
|
||||
sendSmsCode: (phone: string) => Promise<{ success: boolean; message: string }>
|
||||
verifySmsCode: (phone: string, code: string) => Promise<{
|
||||
success: boolean
|
||||
message: string
|
||||
user?: User
|
||||
}>
|
||||
|
||||
// Регистрация организаций
|
||||
registerFulfillmentOrganization: (phone: string, inn: string) => Promise<{
|
||||
success: boolean
|
||||
message: string
|
||||
user?: User
|
||||
}>
|
||||
registerSellerOrganization: (data: {
|
||||
phone: string
|
||||
wbApiKey?: string
|
||||
ozonApiKey?: string
|
||||
ozonClientId?: string
|
||||
}) => Promise<{
|
||||
success: boolean
|
||||
message: string
|
||||
user?: User
|
||||
}>
|
||||
|
||||
// Состояние
|
||||
user: User | null
|
||||
isAuthenticated: boolean
|
||||
isLoading: boolean
|
||||
checkAuth: () => Promise<void>
|
||||
logout: () => void
|
||||
}
|
||||
|
||||
export const useAuth = (): UseAuthReturn => {
|
||||
// Инициализируем состояния с проверкой токена
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [user, setUser] = useState<User | null>(null)
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(() => {
|
||||
// Проверяем наличие токена при инициализации
|
||||
return !!getAuthToken()
|
||||
})
|
||||
const [isCheckingAuth, setIsCheckingAuth] = useState(false) // Защита от повторных вызовов
|
||||
const { refreshApolloClient } = useApolloRefresh()
|
||||
|
||||
const [sendSmsCodeMutation] = useMutation(SEND_SMS_CODE)
|
||||
const [verifySmsCodeMutation] = useMutation(VERIFY_SMS_CODE)
|
||||
const [registerFulfillmentMutation] = useMutation(REGISTER_FULFILLMENT_ORGANIZATION)
|
||||
const [registerSellerMutation] = useMutation(REGISTER_SELLER_ORGANIZATION)
|
||||
|
||||
// Проверка авторизации при инициализации
|
||||
const checkAuth = async () => {
|
||||
if (isCheckingAuth) {
|
||||
console.log('useAuth - checkAuth already in progress, skipping')
|
||||
return
|
||||
}
|
||||
|
||||
const token = getAuthToken()
|
||||
console.log('useAuth - checkAuth called, token exists:', !!token)
|
||||
|
||||
if (!token) {
|
||||
setIsAuthenticated(false)
|
||||
setUser(null)
|
||||
setIsCheckingAuth(false)
|
||||
return
|
||||
}
|
||||
|
||||
setIsCheckingAuth(true)
|
||||
|
||||
try {
|
||||
console.log('useAuth - Making GET_ME query')
|
||||
const { data } = await apolloClient.query({
|
||||
query: GET_ME,
|
||||
errorPolicy: 'all',
|
||||
fetchPolicy: 'network-only' // Всегда делаем свежий запрос
|
||||
})
|
||||
|
||||
console.log('useAuth - GET_ME response:', !!data?.me)
|
||||
if (data?.me) {
|
||||
setUser(data.me)
|
||||
setIsAuthenticated(true)
|
||||
setUserData(data.me)
|
||||
console.log('useAuth - User authenticated:', data.me.phone)
|
||||
} else {
|
||||
setIsAuthenticated(false)
|
||||
setUser(null)
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.log('useAuth - GET_ME error:', error)
|
||||
if ((error as { graphQLErrors?: Array<{ extensions?: { code?: string } }> })?.graphQLErrors?.some((e) => e.extensions?.code === 'UNAUTHENTICATED')) {
|
||||
logout()
|
||||
} else {
|
||||
setIsAuthenticated(false)
|
||||
setUser(null)
|
||||
}
|
||||
} finally {
|
||||
setIsCheckingAuth(false)
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем авторизацию при загрузке компонента только если нет данных пользователя
|
||||
useEffect(() => {
|
||||
const token = getAuthToken()
|
||||
console.log('useAuth - useEffect init, token exists:', !!token, 'user exists:', !!user, 'isChecking:', isCheckingAuth)
|
||||
|
||||
if (token && !user && !isCheckingAuth) {
|
||||
console.log('useAuth - Running checkAuth because token exists but no user data')
|
||||
checkAuth()
|
||||
} else if (!token) {
|
||||
console.log('useAuth - No token, setting unauthenticated state')
|
||||
setIsAuthenticated(false)
|
||||
setUser(null)
|
||||
}
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const sendSmsCode = async (phone: string) => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const { data } = await sendSmsCodeMutation({
|
||||
variables: { phone }
|
||||
})
|
||||
|
||||
return {
|
||||
success: data.sendSmsCode.success,
|
||||
message: data.sendSmsCode.message
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error sending SMS code:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при отправке SMS кода'
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const verifySmsCode = async (phone: string, code: string) => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
console.log('useAuth - Starting verifySmsCode mutation with:', { phone, code })
|
||||
|
||||
const { data } = await verifySmsCodeMutation({
|
||||
variables: { phone, code }
|
||||
})
|
||||
|
||||
console.log('useAuth - GraphQL response data:', data)
|
||||
const result = data.verifySmsCode
|
||||
console.log('useAuth - SMS verification result:', {
|
||||
success: result.success,
|
||||
hasToken: !!result.token,
|
||||
hasUser: !!result.user,
|
||||
message: result.message
|
||||
})
|
||||
|
||||
if (result.success && result.token && result.user) {
|
||||
// Сохраняем токен и данные пользователя
|
||||
console.log('useAuth - Saving token:', result.token ? `${result.token.substring(0, 20)}...` : 'No token')
|
||||
setAuthToken(result.token)
|
||||
setUserData(result.user)
|
||||
|
||||
// Обновляем состояние хука
|
||||
setUser(result.user)
|
||||
setIsAuthenticated(true)
|
||||
console.log('useAuth - State updated: user set, isAuthenticated=true')
|
||||
|
||||
// Проверяем что токен действительно сохранился
|
||||
const savedToken = typeof window !== 'undefined' ? localStorage.getItem('authToken') : null
|
||||
console.log('useAuth - Token saved to localStorage:', savedToken ? `${savedToken.substring(0, 20)}...` : 'Not saved')
|
||||
|
||||
// Принудительно обновляем Apollo Client
|
||||
refreshApolloClient()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: result.message,
|
||||
user: result.user
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: result.message
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error verifying SMS code:', error)
|
||||
console.error('Error details:', {
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
graphQLErrors: (error as { graphQLErrors?: unknown })?.graphQLErrors,
|
||||
networkError: (error as { networkError?: unknown })?.networkError
|
||||
})
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при проверке SMS кода'
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const registerFulfillmentOrganization = async (phone: string, inn: string) => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
|
||||
// Проверяем токен перед запросом
|
||||
const currentToken = typeof window !== 'undefined' ? localStorage.getItem('authToken') : null
|
||||
console.log('useAuth - Token before registerFulfillment request:', currentToken ? `${currentToken.substring(0, 20)}...` : 'No token')
|
||||
|
||||
const { data } = await registerFulfillmentMutation({
|
||||
variables: {
|
||||
input: { phone, inn }
|
||||
}
|
||||
})
|
||||
|
||||
const result = data.registerFulfillmentOrganization
|
||||
|
||||
if (result.success && result.user) {
|
||||
// Обновляем данные пользователя
|
||||
setUserData(result.user)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: result.message,
|
||||
user: result.user
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: result.message
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error registering fulfillment organization:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при регистрации фулфилмент организации'
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const registerSellerOrganization = async (data: {
|
||||
phone: string
|
||||
wbApiKey?: string
|
||||
ozonApiKey?: string
|
||||
ozonClientId?: string
|
||||
}) => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const { data: result } = await registerSellerMutation({
|
||||
variables: { input: data }
|
||||
})
|
||||
|
||||
const registerResult = result.registerSellerOrganization
|
||||
|
||||
if (registerResult.success && registerResult.user) {
|
||||
// Обновляем данные пользователя
|
||||
setUserData(registerResult.user)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: registerResult.message,
|
||||
user: registerResult.user
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message: registerResult.message
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error registering seller organization:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при регистрации селлер организации'
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
console.log('useAuth - Logging out')
|
||||
removeAuthToken()
|
||||
setUser(null)
|
||||
setIsAuthenticated(false)
|
||||
refreshApolloClient()
|
||||
|
||||
// Перенаправляем на главную страницу
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/'
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// SMS методы
|
||||
sendSmsCode,
|
||||
verifySmsCode,
|
||||
|
||||
// Регистрация организаций
|
||||
registerFulfillmentOrganization,
|
||||
registerSellerOrganization,
|
||||
|
||||
// Состояние
|
||||
user,
|
||||
isAuthenticated,
|
||||
isLoading,
|
||||
checkAuth,
|
||||
logout
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user