130 lines
4.5 KiB
TypeScript
130 lines
4.5 KiB
TypeScript
"use client"
|
|
|
|
import React, { createContext, useContext, useEffect, useState } from 'react'
|
|
import { safeLocalStorage } from '@/lib/utils'
|
|
import { logout as logoutApi } from '@/components/apis/authApiClients'
|
|
|
|
interface User {
|
|
id: string
|
|
email: string
|
|
username: string
|
|
role?: 'user' | 'admin'
|
|
}
|
|
|
|
interface AuthContextValue {
|
|
user: User | null
|
|
isAdmin: boolean
|
|
isLoading: boolean
|
|
setUserFromApi: (user: User) => void
|
|
logout: () => Promise<void>
|
|
}
|
|
|
|
const AuthContext = createContext<AuthContextValue | undefined>(undefined)
|
|
|
|
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
const [user, setUser] = useState<User | null>(null)
|
|
const [isLoading, setIsLoading] = useState(true)
|
|
|
|
useEffect(() => {
|
|
const stored = safeLocalStorage.getItem('codenuk_user')
|
|
const accessToken = safeLocalStorage.getItem('accessToken')
|
|
const refreshToken = safeLocalStorage.getItem('refreshToken')
|
|
|
|
console.log('🔐 [AuthContext] Checking localStorage for user data...')
|
|
console.log('🔐 [AuthContext] Stored user data:', stored)
|
|
console.log('🔐 [AuthContext] Access token exists:', !!accessToken)
|
|
console.log('🔐 [AuthContext] Refresh token exists:', !!refreshToken)
|
|
console.log('🔐 [AuthContext] Token details:', {
|
|
accessTokenLength: accessToken?.length || 0,
|
|
refreshTokenLength: refreshToken?.length || 0,
|
|
timestamp: new Date().toISOString()
|
|
})
|
|
|
|
// Only restore user if we have both user data AND at least one token
|
|
if (stored && (accessToken || refreshToken)) {
|
|
try {
|
|
const userData = JSON.parse(stored)
|
|
console.log('🔐 [AuthContext] Restoring user session:', userData?.username)
|
|
console.log('🔐 [AuthContext] User ID from localStorage:', userData?.id)
|
|
setUser(userData)
|
|
} catch (error) {
|
|
console.error('Failed to parse stored user data:', error)
|
|
safeLocalStorage.removeItem('codenuk_user')
|
|
}
|
|
} else {
|
|
console.log('🔐 [AuthContext] No valid user session found')
|
|
// Clear any partial data
|
|
if (stored && !accessToken && !refreshToken) {
|
|
console.log('🔐 [AuthContext] Clearing orphaned user data without tokens')
|
|
safeLocalStorage.removeItem('codenuk_user')
|
|
}
|
|
}
|
|
setIsLoading(false)
|
|
}, [])
|
|
|
|
// Listen for storage changes to sync auth state across tabs
|
|
useEffect(() => {
|
|
const handleStorageChange = (e: StorageEvent) => {
|
|
if (e.key === 'codenuk_user' || e.key === 'accessToken' || e.key === 'refreshToken') {
|
|
console.log('🔐 [AuthContext] Storage changed, re-checking auth state')
|
|
const stored = safeLocalStorage.getItem('codenuk_user')
|
|
const accessToken = safeLocalStorage.getItem('accessToken')
|
|
const refreshToken = safeLocalStorage.getItem('refreshToken')
|
|
|
|
if (stored && (accessToken || refreshToken)) {
|
|
try {
|
|
const userData = JSON.parse(stored)
|
|
setUser(userData)
|
|
} catch (error) {
|
|
console.error('Failed to parse user data after storage change:', error)
|
|
setUser(null)
|
|
}
|
|
} else {
|
|
setUser(null)
|
|
}
|
|
}
|
|
}
|
|
|
|
window.addEventListener('storage', handleStorageChange)
|
|
return () => window.removeEventListener('storage', handleStorageChange)
|
|
}, [])
|
|
|
|
const setUserFromApi = (u: User) => {
|
|
console.log('🔐 [AuthContext] Setting user from API:', u)
|
|
console.log('🔐 [AuthContext] User ID being set:', u?.id)
|
|
setUser(u)
|
|
safeLocalStorage.setItem('codenuk_user', JSON.stringify(u))
|
|
console.log('🔐 [AuthContext] User data saved to localStorage')
|
|
}
|
|
|
|
const logout = async () => {
|
|
try {
|
|
// Call the logout API to invalidate tokens on the server
|
|
await logoutApi()
|
|
} catch (error) {
|
|
console.error('Logout API call failed:', error)
|
|
// Continue with local logout even if API call fails
|
|
} finally {
|
|
// Always clear local data
|
|
setUser(null)
|
|
safeLocalStorage.removeItem('codenuk_user')
|
|
safeLocalStorage.removeItem('accessToken')
|
|
safeLocalStorage.removeItem('refreshToken')
|
|
|
|
// Redirect to signin page
|
|
window.location.href = '/signin'
|
|
}
|
|
}
|
|
|
|
return (
|
|
<AuthContext.Provider value={{ user, isAdmin: user?.role === 'admin', isLoading, setUserFromApi, logout }}>
|
|
{children}
|
|
</AuthContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useAuth() {
|
|
const ctx = useContext(AuthContext)
|
|
if (!ctx) throw new Error('useAuth must be used within AuthProvider')
|
|
return ctx
|
|
} |