Merge pull request #277 from getmaxun/logout

feat: auto logout
This commit is contained in:
Karishma Shukla
2024-12-20 22:02:03 +05:30
committed by GitHub

View File

@@ -1,4 +1,4 @@
import { useReducer, createContext, useEffect } from 'react';
import { useReducer, createContext, useEffect, useCallback } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';
import { apiUrl } from "../apiConfig";
@@ -14,12 +14,16 @@ interface ActionType {
type InitialStateType = {
user: any;
lastActivityTime?: number;
};
const initialState = {
user: null,
lastActivityTime: Date.now(),
};
const AUTO_LOGOUT_TIME = 4 * 60 * 60 * 1000; // 4 hours in milliseconds
const AuthContext = createContext<{
state: InitialStateType;
dispatch: React.Dispatch<ActionType>;
@@ -34,11 +38,13 @@ const reducer = (state: InitialStateType, action: ActionType) => {
return {
...state,
user: action.payload,
lastActivityTime: Date.now(),
};
case 'LOGOUT':
return {
...state,
user: null,
lastActivityTime: undefined,
};
default:
return state;
@@ -50,6 +56,39 @@ const AuthProvider = ({ children }: AuthProviderProps) => {
const navigate = useNavigate();
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(() => {
const storedUser = window.localStorage.getItem('user');
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(
function (response) {
return response;
},
function (error) {
const res = error.response;
if (res.status === 401 && res.config && !res.config.__isRetryRequest) {
return new Promise((resolve, reject) => {
axios
.get(`${apiUrl}/auth/logout`)
if (res?.status === 401 && res.config && !res.config.__isRetryRequest) {
return new Promise((_, reject) => {
handleLogout()
.then(() => {
console.log('/401 error > logout');
dispatch({ type: 'LOGOUT' });
window.localStorage.removeItem('user');
navigate('/login');
reject(error);
})
.catch((err) => {
console.error('AXIOS INTERCEPTORS ERROR:', err);