FlowWeb / src /context /AuthContext.jsx
danylokhodus's picture
feat: add heartbeat ping to keep HF Space awake
b1dd449
Raw
History Blame Contribute Delete
4.39 kB
import React, { createContext, useContext, useState, useEffect } from 'react';
import axios from 'axios';
const AuthContext = createContext();
export const useAuth = () => useContext(AuthContext);
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const API_URL = import.meta.env.VITE_API_URL;
// Keep HF Space alive — ping every 25 minutes to prevent sleeping
useEffect(() => {
const ping = () => axios.get(`${API_URL}/users`).catch(() => {});
ping(); // initial ping on load
const interval = setInterval(ping, 25 * 60 * 1000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const initializeAuth = async () => {
console.log('[Auth] Initializing authentication...');
const token = localStorage.getItem('token');
if (token) {
console.log('[Auth] Found token in localStorage, setting Authorization header.');
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
try {
console.log('[Auth] Fetching user profile from:', `${API_URL}/users/me`);
const response = await axios.get(`${API_URL}/users/me`);
console.log('[Auth] Profile fetched successfully:', response.data);
setUser(response.data);
} catch (error) {
console.error('[Auth] Failed to fetch profile:', error);
// Only logout on explicit authorization failures (e.g. 401, 403). Keep session on server network errors.
if (error.response && (error.response.status === 401 || error.response.status === 403 || error.response.status === 404)) {
console.warn('[Auth] Token is expired, invalid, or user was not found on server. Logging out.');
logout();
} else {
console.warn('[Auth] Server is unreachable or returned network error. Retaining session.');
const cachedUser = localStorage.getItem('user');
if (cachedUser) {
try {
const parsed = JSON.parse(cachedUser);
console.log('[Auth] Loaded cached user from localStorage:', parsed);
setUser(parsed);
} catch (parseErr) {
console.error('[Auth] Failed to parse cached user:', parseErr);
logout();
}
}
}
}
} else {
console.log('[Auth] No token found in localStorage.');
}
setLoading(false);
};
initializeAuth();
}, []);
const login = async (email, password) => {
try {
const response = await axios.post(`${API_URL}/users/login`, { email, password });
handleAuthResponse(response.data);
return { success: true };
} catch (error) {
return { success: false, message: error.response?.data?.message || 'Login failed' };
}
};
const register = async (email, password, displayName) => {
try {
const response = await axios.post(`${API_URL}/users/register`, { email, password, displayName });
handleAuthResponse(response.data);
return { success: true };
} catch (error) {
return { success: false, message: error.response?.data?.message || 'Registration failed' };
}
};
const googleLogin = async (idToken) => {
try {
const response = await axios.post(`${API_URL}/users/google-login`, { idToken });
handleAuthResponse(response.data);
return { success: true };
} catch (error) {
return { success: false, message: error.response?.data?.message || 'Google Login failed' };
}
};
const logout = () => {
setUser(null);
localStorage.removeItem('user');
localStorage.removeItem('token');
delete axios.defaults.headers.common['Authorization'];
};
const updateUser = (updatedUser) => {
setUser(updatedUser);
localStorage.setItem('user', JSON.stringify(updatedUser));
};
const handleAuthResponse = (data) => {
const { user, token } = data;
setUser(user);
localStorage.setItem('user', JSON.stringify(user));
localStorage.setItem('token', token);
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
};
return (
<AuthContext.Provider value={{ user, loading, login, register, googleLogin, logout, updateUser }}>
{!loading && children}
</AuthContext.Provider>
);
};