'use client' import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react' import { useLocale } from 'next-intl' import { useStore } from '@/lib/store' import { isTokenExpired, clearExpiredToken } from '@/lib/auth/client' interface User { id: string email: string name?: string role: string theme: string fontSize: string createdAt: Date updatedAt: Date lastLoginAt?: Date } interface AuthContextType { user: User | null isAuthenticated: boolean isLoading: boolean login: (email: string, password: string) => Promise<{ success: boolean; error?: string }> register: (email: string, password: string, name?: string) => Promise<{ success: boolean; error?: string }> logout: () => Promise refreshUser: () => Promise } const AuthContext = createContext(undefined) interface AuthProviderProps { children: ReactNode } export function AuthProvider({ children }: AuthProviderProps) { const [isLoading, setIsLoading] = useState(true) const [initialized, setInitialized] = useState(false) const locale = useLocale() const { user, setUser } = useStore() const refreshUser = async (forceRefresh = false) => { const token = localStorage.getItem('authToken') if (!token) { setUser(null) setIsLoading(false) return } // Check if token is expired before making request if (isTokenExpired(token)) { console.log('Token expired in refreshUser, clearing auth state') localStorage.removeItem('authToken') setUser(null) setIsLoading(false) return } // If we already have a user and this isn't a forced refresh, don't re-fetch if (user && !forceRefresh) { setIsLoading(false) return } try { const response = await fetch(`/api/auth/me?locale=${locale}`, { headers: { 'Authorization': `Bearer ${token}` } }) if (response.ok) { const data = await response.json() setUser(data.user) } else { // Token is invalid or expired, get error details try { const errorData = await response.json() console.log('Server returned 401 error:', errorData) } catch (e) { console.log('Server returned 401 without JSON body, status:', response.status) } console.log('Token expired or invalid, clearing auth state') localStorage.removeItem('authToken') setUser(null) } } catch (error) { console.error('Auth check failed:', error) // Network error or other issues, clean up auth state localStorage.removeItem('authToken') setUser(null) } finally { setIsLoading(false) } } // Debug database schema const debugSchema = async () => { try { const response = await fetch('/api/debug/schema') const debug = await response.json() console.log('Database schema info:', debug) } catch (e) { console.log('Schema debug failed:', e) } } // Debug user lookup const debugUser = async (userId: string) => { try { const response = await fetch('/api/debug/user', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userId }) }) const debug = await response.json() console.log('User debug info:', debug) } catch (e) { console.log('User debug failed:', e) } } // Debug token validation const debugToken = async (token: string) => { try { const response = await fetch('/api/debug/token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token }) }) const debug = await response.json() console.log('Token debug info:', debug) // Log more details about the token payload if (debug.payload) { console.log('Token payload:', debug.payload) // Debug user lookup if (debug.payload.userId) { debugUser(debug.payload.userId) } } if (debug.verificationResult) { console.log('Verification result:', debug.verificationResult) } } catch (e) { console.log('Token debug failed:', e) } } // Clear expired tokens and sync state immediately on mount useEffect(() => { if (typeof window !== 'undefined') { const token = localStorage.getItem('authToken') console.log('Auth mount check - token exists:', !!token) if (token) { console.log('Token preview:', token.substring(0, 50) + '...') // Debug the database schema and token on server side debugSchema() debugToken(token) const expired = isTokenExpired(token) console.log('Token expired:', expired) if (expired) { console.log('Clearing expired token and user state on mount') localStorage.removeItem('authToken') setUser(null) } } else if (user) { console.log('No token but user exists in store, clearing user state') setUser(null) } clearExpiredToken() } }, []) // Initialize auth state only once on mount useEffect(() => { if (!initialized && typeof window !== 'undefined') { const token = localStorage.getItem('authToken') console.log('Initialization flow - token exists:', !!token) if (token) { // Check if token is expired before making request if (isTokenExpired(token)) { console.log('Token expired during initialization, clearing auth state') localStorage.removeItem('authToken') setUser(null) setIsLoading(false) } else { console.log('Token is valid, calling refreshUser()') // Token appears valid, try to refresh user data // refreshUser will handle server-side validation failures refreshUser() } } else { console.log('No token found, clearing user state') // No token, clear any stale user data setUser(null) setIsLoading(false) } setInitialized(true) } }, [initialized]) // Handle hydration issues - ensure we stop loading once initialized useEffect(() => { if (initialized && isLoading) { const token = localStorage.getItem('authToken') if (!token) { setIsLoading(false) } } }, [initialized, isLoading]) const login = async (email: string, password: string): Promise<{ success: boolean; error?: string }> => { try { const response = await fetch(`/api/auth/login?locale=${locale}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, password }), }) const data = await response.json() if (response.ok) { localStorage.setItem('authToken', data.token) setUser(data.user) return { success: true } } else { return { success: false, error: data.error } } } catch (error) { console.error('Login error:', error) return { success: false, error: locale === 'en' ? 'Login failed' : 'Autentificare eșuată' } } } const register = async (email: string, password: string, name?: string): Promise<{ success: boolean; error?: string }> => { try { const response = await fetch(`/api/auth/register?locale=${locale}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, password, name }), }) const data = await response.json() if (response.ok) { localStorage.setItem('authToken', data.token) setUser(data.user) return { success: true } } else { return { success: false, error: data.error } } } catch (error) { console.error('Registration error:', error) return { success: false, error: locale === 'en' ? 'Registration failed' : 'Înregistrare eșuată' } } } const logout = async () => { try { const token = localStorage.getItem('authToken') if (token) { await fetch('/api/auth/logout', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }) } } catch (error) { console.error('Logout error:', error) } finally { localStorage.removeItem('authToken') setUser(null) } } const value: AuthContextType = { user, isAuthenticated: !!user, isLoading, login, register, logout, refreshUser: () => refreshUser(true) } return ( {children} ) } export function useAuth(): AuthContextType { const context = useContext(AuthContext) if (context === undefined) { throw new Error('useAuth must be used within an AuthProvider') } return context }