@@ -1,4 +1,4 @@
|
|||||||
import { useReducer, createContext, useEffect } from 'react';
|
import { useReducer, createContext, useEffect, useCallback } from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { apiUrl } from "../apiConfig";
|
import { apiUrl } from "../apiConfig";
|
||||||
@@ -14,12 +14,16 @@ interface ActionType {
|
|||||||
|
|
||||||
type InitialStateType = {
|
type InitialStateType = {
|
||||||
user: any;
|
user: any;
|
||||||
|
lastActivityTime?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
user: null,
|
user: null,
|
||||||
|
lastActivityTime: Date.now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AUTO_LOGOUT_TIME = 4 * 60 * 60 * 1000; // 4 hours in milliseconds
|
||||||
|
|
||||||
const AuthContext = createContext<{
|
const AuthContext = createContext<{
|
||||||
state: InitialStateType;
|
state: InitialStateType;
|
||||||
dispatch: React.Dispatch<ActionType>;
|
dispatch: React.Dispatch<ActionType>;
|
||||||
@@ -34,11 +38,13 @@ const reducer = (state: InitialStateType, action: ActionType) => {
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
user: action.payload,
|
user: action.payload,
|
||||||
|
lastActivityTime: Date.now(),
|
||||||
};
|
};
|
||||||
case 'LOGOUT':
|
case 'LOGOUT':
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
user: null,
|
user: null,
|
||||||
|
lastActivityTime: undefined,
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
@@ -50,6 +56,39 @@ const AuthProvider = ({ children }: AuthProviderProps) => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
axios.defaults.withCredentials = true;
|
axios.defaults.withCredentials = true;
|
||||||
|
|
||||||
|
const handleLogout = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
await axios.get(`${apiUrl}/auth/logout`);
|
||||||
|
dispatch({ type: 'LOGOUT' });
|
||||||
|
window.localStorage.removeItem('user');
|
||||||
|
navigate('/login');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Logout error:', err);
|
||||||
|
}
|
||||||
|
}, [navigate]);
|
||||||
|
|
||||||
|
const checkAutoLogout = useCallback(() => {
|
||||||
|
if (state.user && state.lastActivityTime) {
|
||||||
|
const currentTime = Date.now();
|
||||||
|
const timeSinceLastActivity = currentTime - state.lastActivityTime;
|
||||||
|
|
||||||
|
if (timeSinceLastActivity >= AUTO_LOGOUT_TIME) {
|
||||||
|
handleLogout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [state.user, state.lastActivityTime, handleLogout]);
|
||||||
|
|
||||||
|
// Update last activity time on user interactions
|
||||||
|
const updateActivityTime = useCallback(() => {
|
||||||
|
if (state.user) {
|
||||||
|
dispatch({
|
||||||
|
type: 'LOGIN',
|
||||||
|
payload: state.user // Reuse existing user data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [state.user]);
|
||||||
|
|
||||||
|
// Initialize user from localStorage
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const storedUser = window.localStorage.getItem('user');
|
const storedUser = window.localStorage.getItem('user');
|
||||||
if (storedUser) {
|
if (storedUser) {
|
||||||
@@ -57,21 +96,54 @@ const AuthProvider = ({ children }: AuthProviderProps) => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Set up activity listeners
|
||||||
|
useEffect(() => {
|
||||||
|
if (state.user) {
|
||||||
|
// List of events to track for user activity
|
||||||
|
const events = ['mousedown', 'keydown', 'scroll', 'touchstart'];
|
||||||
|
|
||||||
|
// Throttled event handler
|
||||||
|
let timeoutId: NodeJS.Timeout;
|
||||||
|
const handleActivity = () => {
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
timeoutId = setTimeout(updateActivityTime, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add event listeners
|
||||||
|
events.forEach(event => {
|
||||||
|
window.addEventListener(event, handleActivity);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up periodic check for auto logout
|
||||||
|
const checkInterval = setInterval(checkAutoLogout, 60000); // Check every minute
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
return () => {
|
||||||
|
events.forEach(event => {
|
||||||
|
window.removeEventListener(event, handleActivity);
|
||||||
|
});
|
||||||
|
clearInterval(checkInterval);
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [state.user, updateActivityTime, checkAutoLogout]);
|
||||||
|
|
||||||
axios.interceptors.response.use(
|
axios.interceptors.response.use(
|
||||||
function (response) {
|
function (response) {
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
const res = error.response;
|
const res = error.response;
|
||||||
if (res.status === 401 && res.config && !res.config.__isRetryRequest) {
|
if (res?.status === 401 && res.config && !res.config.__isRetryRequest) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((_, reject) => {
|
||||||
axios
|
handleLogout()
|
||||||
.get(`${apiUrl}/auth/logout`)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('/401 error > logout');
|
console.log('/401 error > logout');
|
||||||
dispatch({ type: 'LOGOUT' });
|
reject(error);
|
||||||
window.localStorage.removeItem('user');
|
|
||||||
navigate('/login');
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('AXIOS INTERCEPTORS ERROR:', err);
|
console.error('AXIOS INTERCEPTORS ERROR:', err);
|
||||||
|
|||||||
Reference in New Issue
Block a user