|
|
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; |
|
|
import { api } from '../services/api'; |
|
|
|
|
|
interface User { |
|
|
id: string; |
|
|
username: string; |
|
|
email: string; |
|
|
role: 'student' | 'instructor' | 'admin' | 'visitor'; |
|
|
targetCultures: string[]; |
|
|
nativeLanguage?: string; |
|
|
} |
|
|
|
|
|
interface AuthContextType { |
|
|
user: User | null; |
|
|
loading: boolean; |
|
|
login: (email: string, password: string) => Promise<void>; |
|
|
register: (userData: RegisterData) => Promise<void>; |
|
|
logout: () => void; |
|
|
updateProfile: (data: Partial<User>) => Promise<void>; |
|
|
} |
|
|
|
|
|
interface RegisterData { |
|
|
username: string; |
|
|
email: string; |
|
|
password: string; |
|
|
role?: 'student' | 'instructor' | 'admin'; |
|
|
targetCultures?: string[]; |
|
|
nativeLanguage?: string; |
|
|
} |
|
|
|
|
|
const AuthContext = createContext<AuthContextType | undefined>(undefined); |
|
|
|
|
|
export const useAuth = () => { |
|
|
const context = useContext(AuthContext); |
|
|
if (context === undefined) { |
|
|
throw new Error('useAuth must be used within an AuthProvider'); |
|
|
} |
|
|
return context; |
|
|
}; |
|
|
|
|
|
interface AuthProviderProps { |
|
|
children: ReactNode; |
|
|
} |
|
|
|
|
|
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => { |
|
|
const [user, setUser] = useState<User | null>(null); |
|
|
const [loading, setLoading] = useState(true); |
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
const token = localStorage.getItem('token'); |
|
|
if (token) { |
|
|
api.defaults.headers.common['Authorization'] = `Bearer ${token}`; |
|
|
fetchUser(); |
|
|
} else { |
|
|
|
|
|
setupVisitorAuth(); |
|
|
} |
|
|
}, []); |
|
|
|
|
|
const setupVisitorAuth = async () => { |
|
|
try { |
|
|
const response = await api.post('/api/auth/login', { |
|
|
email: 'visitor@example.com' |
|
|
}); |
|
|
const { token, user } = response.data; |
|
|
|
|
|
localStorage.setItem('token', token); |
|
|
api.defaults.headers.common['Authorization'] = `Bearer ${token}`; |
|
|
setUser(user); |
|
|
} catch (error) { |
|
|
console.error('Failed to setup visitor auth:', error); |
|
|
|
|
|
const fallbackToken = `visitor_${Date.now()}`; |
|
|
localStorage.setItem('token', fallbackToken); |
|
|
api.defaults.headers.common['Authorization'] = `Bearer ${fallbackToken}`; |
|
|
setUser({ |
|
|
id: 'visitor', |
|
|
username: 'Visitor', |
|
|
email: 'visitor@example.com', |
|
|
role: 'visitor', |
|
|
targetCultures: [] |
|
|
}); |
|
|
} finally { |
|
|
setLoading(false); |
|
|
} |
|
|
}; |
|
|
|
|
|
const fetchUser = async () => { |
|
|
try { |
|
|
const response = await api.get('/api/auth/profile'); |
|
|
setUser(response.data); |
|
|
} catch (error) { |
|
|
console.error('Failed to fetch user:', error); |
|
|
localStorage.removeItem('token'); |
|
|
delete api.defaults.headers.common['Authorization']; |
|
|
|
|
|
setupVisitorAuth(); |
|
|
return; |
|
|
} finally { |
|
|
setLoading(false); |
|
|
} |
|
|
}; |
|
|
|
|
|
const login = async (email: string, password: string) => { |
|
|
try { |
|
|
const response = await api.post('/api/auth/login', { email }); |
|
|
const { token, user } = response.data; |
|
|
|
|
|
localStorage.setItem('token', token); |
|
|
localStorage.setItem('user', JSON.stringify(user)); |
|
|
api.defaults.headers.common['Authorization'] = `Bearer ${token}`; |
|
|
setUser(user); |
|
|
} catch (error: any) { |
|
|
console.error('Login error:', error); |
|
|
throw new Error(error.response?.data?.error || 'Login failed'); |
|
|
} |
|
|
}; |
|
|
|
|
|
const register = async (userData: RegisterData) => { |
|
|
try { |
|
|
const response = await api.post('/api/auth/register', userData); |
|
|
const { token, user } = response.data; |
|
|
|
|
|
localStorage.setItem('token', token); |
|
|
localStorage.setItem('user', JSON.stringify(user)); |
|
|
api.defaults.headers.common['Authorization'] = `Bearer ${token}`; |
|
|
setUser(user); |
|
|
} catch (error: any) { |
|
|
throw new Error(error.response?.data?.error || 'Registration failed'); |
|
|
} |
|
|
}; |
|
|
|
|
|
const logout = () => { |
|
|
localStorage.removeItem('token'); |
|
|
delete api.defaults.headers.common['Authorization']; |
|
|
setUser(null); |
|
|
|
|
|
setupVisitorAuth(); |
|
|
}; |
|
|
|
|
|
const updateProfile = async (data: Partial<User>) => { |
|
|
try { |
|
|
const response = await api.put('/api/auth/profile', data); |
|
|
setUser(response.data); |
|
|
} catch (error: any) { |
|
|
throw new Error(error.response?.data?.error || 'Profile update failed'); |
|
|
} |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<AuthContext.Provider value={{ |
|
|
user, |
|
|
loading, |
|
|
login, |
|
|
register, |
|
|
logout, |
|
|
updateProfile |
|
|
}}> |
|
|
{children} |
|
|
</AuthContext.Provider> |
|
|
); |
|
|
}; |