Spaces:
Running
Running
| import { createContext, useContext, useState, useEffect, useCallback } from 'react'; | |
| const AuthContext = createContext(); | |
| const API_BASE = import.meta.env.VITE_API_URL || ''; | |
| export function AuthProvider({ children }) { | |
| const [user, setUser] = useState(null); | |
| const [token, setToken] = useState(null); | |
| const [loading, setLoading] = useState(true); | |
| // On mount, check if we have a stored token and verify it | |
| useEffect(() => { | |
| const storedToken = sessionStorage.getItem('ot-token'); | |
| const storedUser = sessionStorage.getItem('ot-user'); | |
| if (storedToken && storedUser) { | |
| // Verify the token is still valid | |
| fetch(`${API_BASE}/api/verify`, { | |
| headers: { Authorization: `Bearer ${storedToken}` }, | |
| }) | |
| .then(res => { | |
| if (res.ok) { | |
| setToken(storedToken); | |
| setUser(storedUser); | |
| } else { | |
| // Token invalid/expired — clear storage | |
| sessionStorage.removeItem('ot-token'); | |
| sessionStorage.removeItem('ot-user'); | |
| } | |
| }) | |
| .catch(() => { | |
| sessionStorage.removeItem('ot-token'); | |
| sessionStorage.removeItem('ot-user'); | |
| }) | |
| .finally(() => setLoading(false)); | |
| } else { | |
| setLoading(false); | |
| } | |
| }, []); | |
| const login = useCallback(async (username, password) => { | |
| const res = await fetch(`${API_BASE}/api/login`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ username, password }), | |
| }); | |
| if (!res.ok) { | |
| const err = await res.json().catch(() => ({})); | |
| throw new Error(err.detail || 'Login failed'); | |
| } | |
| const data = await res.json(); | |
| setToken(data.token); | |
| setUser(data.username); | |
| sessionStorage.setItem('ot-token', data.token); | |
| sessionStorage.setItem('ot-user', data.username); | |
| return data; | |
| }, []); | |
| const logout = useCallback(() => { | |
| setToken(null); | |
| setUser(null); | |
| sessionStorage.removeItem('ot-token'); | |
| sessionStorage.removeItem('ot-user'); | |
| }, []); | |
| const getAuthHeaders = useCallback(() => { | |
| if (!token) return {}; | |
| return { Authorization: `Bearer ${token}` }; | |
| }, [token]); | |
| const isAuthenticated = !!token && !!user; | |
| return ( | |
| <AuthContext.Provider value={{ user, token, loading, isAuthenticated, login, logout, getAuthHeaders }}> | |
| {children} | |
| </AuthContext.Provider> | |
| ); | |
| } | |
| export function useAuth() { | |
| const ctx = useContext(AuthContext); | |
| if (!ctx) throw new Error('useAuth must be used within AuthProvider'); | |
| return ctx; | |
| } | |