mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-19 18:00:15 +01:00
🔒 fix: Robust Cache Reset on User Logout (#1324)
* refactor(Logout): rely on hooks for mutation behavior * fix: logging out now correctly resets cache, disallowing any cache mixing between the next logged in user on the same browser * chore: remove additional localStorage values on logout
This commit is contained in:
parent
583e978a82
commit
968b8ccdbd
10 changed files with 109 additions and 90 deletions
|
|
@ -12,13 +12,13 @@ import {
|
|||
TLoginResponse,
|
||||
setTokenHeader,
|
||||
useLoginUserMutation,
|
||||
useLogoutUserMutation,
|
||||
useGetUserQuery,
|
||||
useRefreshTokenMutation,
|
||||
TLoginUser,
|
||||
} from 'librechat-data-provider';
|
||||
import { TAuthConfig, TUserContext, TAuthContext, TResError } from '~/common';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { TAuthConfig, TUserContext, TAuthContext, TResError } from '~/common';
|
||||
import { useLogoutUserMutation } from '~/data-provider';
|
||||
import useTimeout from './useTimeout';
|
||||
|
||||
const AuthContext = createContext<TAuthContext | undefined>(undefined);
|
||||
|
|
@ -30,20 +30,11 @@ const AuthContextProvider = ({
|
|||
authConfig?: TAuthConfig;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const [user, setUser] = useState<TUser | undefined>(undefined);
|
||||
const [token, setToken] = useState<string | undefined>(undefined);
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const loginUser = useLoginUserMutation();
|
||||
const logoutUser = useLogoutUserMutation();
|
||||
const userQuery = useGetUserQuery({ enabled: !!token });
|
||||
const refreshToken = useRefreshTokenMutation();
|
||||
|
||||
const doSetError = useTimeout({ callback: (error) => setError(error as string | undefined) });
|
||||
|
||||
const setUserContext = useCallback(
|
||||
(userContext: TUserContext) => {
|
||||
const { token, isAuthenticated, user, redirect } = userContext;
|
||||
|
|
@ -60,6 +51,32 @@ const AuthContextProvider = ({
|
|||
},
|
||||
[navigate],
|
||||
);
|
||||
const doSetError = useTimeout({ callback: (error) => setError(error as string | undefined) });
|
||||
|
||||
const loginUser = useLoginUserMutation();
|
||||
const logoutUser = useLogoutUserMutation({
|
||||
onSuccess: () => {
|
||||
setUserContext({
|
||||
token: undefined,
|
||||
isAuthenticated: false,
|
||||
user: undefined,
|
||||
redirect: '/login',
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
doSetError((error as Error).message);
|
||||
setUserContext({
|
||||
token: undefined,
|
||||
isAuthenticated: false,
|
||||
user: undefined,
|
||||
redirect: '/login',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const logout = useCallback(() => logoutUser.mutate(undefined), [logoutUser]);
|
||||
const userQuery = useGetUserQuery({ enabled: !!token });
|
||||
const refreshToken = useRefreshTokenMutation();
|
||||
|
||||
const login = (data: TLoginUser) => {
|
||||
loginUser.mutate(data, {
|
||||
|
|
@ -75,28 +92,6 @@ const AuthContextProvider = ({
|
|||
});
|
||||
};
|
||||
|
||||
const logout = useCallback(() => {
|
||||
logoutUser.mutate(undefined, {
|
||||
onSuccess: () => {
|
||||
setUserContext({
|
||||
token: undefined,
|
||||
isAuthenticated: false,
|
||||
user: undefined,
|
||||
redirect: '/login',
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
doSetError((error as Error).message);
|
||||
setUserContext({
|
||||
token: undefined,
|
||||
isAuthenticated: false,
|
||||
user: undefined,
|
||||
redirect: '/login',
|
||||
});
|
||||
},
|
||||
});
|
||||
}, [setUserContext, doSetError, logoutUser]);
|
||||
|
||||
const silentRefresh = useCallback(() => {
|
||||
if (authConfig?.test) {
|
||||
console.log('Test mode. Skipping silent refresh.');
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import {
|
||||
QueryKeys,
|
||||
modularEndpoints,
|
||||
useGetPresetsQuery,
|
||||
useCreatePresetMutation,
|
||||
} from 'librechat-data-provider';
|
||||
import { QueryKeys, modularEndpoints, useCreatePresetMutation } from 'librechat-data-provider';
|
||||
import filenamify from 'filenamify';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import exportFromJSON from 'export-from-json';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import type { TPreset } from 'librechat-data-provider';
|
||||
import { useUpdatePresetMutation, useDeletePresetMutation } from '~/data-provider';
|
||||
import {
|
||||
useUpdatePresetMutation,
|
||||
useDeletePresetMutation,
|
||||
useGetPresetsQuery,
|
||||
} from '~/data-provider';
|
||||
import { useChatContext, useToastContext } from '~/Providers';
|
||||
import useNavigateToConvo from '~/hooks/useNavigateToConvo';
|
||||
import useDefaultConvo from '~/hooks/useDefaultConvo';
|
||||
|
|
@ -22,22 +21,28 @@ import store from '~/store';
|
|||
|
||||
export default function usePresets() {
|
||||
const localize = useLocalize();
|
||||
const { user } = useAuthContext();
|
||||
const hasLoaded = useRef(false);
|
||||
const queryClient = useQueryClient();
|
||||
const { showToast } = useToastContext();
|
||||
const hasLoaded = useRef(false);
|
||||
const { user, isAuthenticated } = useAuthContext();
|
||||
|
||||
const [_defaultPreset, setDefaultPreset] = useRecoilState(store.defaultPreset);
|
||||
const setPresetModalVisible = useSetRecoilState(store.presetModalVisible);
|
||||
const { preset, conversation, newConversation, setPreset } = useChatContext();
|
||||
const presetsQuery = useGetPresetsQuery({ enabled: !!user });
|
||||
const presetsQuery = useGetPresetsQuery({ enabled: !!user && isAuthenticated });
|
||||
|
||||
useEffect(() => {
|
||||
if (_defaultPreset || !presetsQuery.data || hasLoaded.current) {
|
||||
const { data: presets } = presetsQuery;
|
||||
if (_defaultPreset || !presets || hasLoaded.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultPreset = presetsQuery.data.find((p) => p.defaultPreset);
|
||||
if (presets && presets.length > 0 && user && presets[0].user !== user?.id) {
|
||||
presetsQuery.refetch();
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultPreset = presets.find((p) => p.defaultPreset);
|
||||
if (!defaultPreset) {
|
||||
hasLoaded.current = true;
|
||||
return;
|
||||
|
|
@ -49,7 +54,7 @@ export default function usePresets() {
|
|||
hasLoaded.current = true;
|
||||
// dependencies are stable and only needed once
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [presetsQuery.data]);
|
||||
}, [presetsQuery.data, user]);
|
||||
|
||||
const setPresets = useCallback(
|
||||
(presets: TPreset[]) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue