From 45dd2b262fe5c6651d1d54d9f0a2f3fc5a7e758c Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Mon, 3 Feb 2025 10:53:04 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=82=20feat:=20OpenID=20Logout=20Redire?= =?UTF-8?q?ct=20to=20`end=5Fsession=5Fendpoint`=20(#5626)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP: end session endpoint * refactor: move useGetBannerQuery outside of package * refactor: add queriesEnabled and move useGetEndpointsConfigQuery to data-provider (local) * refactor: move useGetEndpointsQuery import to data-provider * refactor: relocate useGetEndpointsQuery import to improve module organization * refactor: move `useGetStartupConfig` from package to `~/data-provider` * refactor: move useGetUserBalance to data-provider and update imports * refactor: update query enabled conditions to include config check * refactor: remove unused useConfigOverride import from useAppStartup * refactor: integrate queriesEnabled state into file and search queries and move useGetSearchEnabledQuery to data-provider (local) * refactor: move useGetUserQuery to data-provider and update imports * refactor: enhance loginUser mutation with success and error handling as pass in options to hook * refactor: update enabled condition in queries to handle undefined config * refactor: enhance authentication mutations with queriesEnabled state management * refactor: improve conditional rendering for error messages and feature flags in Login component * refactor: remove unused queriesEnabled state from AuthContextProvider * refactor: implement queriesEnabled state management in LoginLayout with timeout handling * refactor: add conditional check for end session endpoint in OpenID strategy * ci: fix tests after changes * refactor: remove endSessionEndpoint from user schema and update logoutController to use OpenID issuer's end_session_endpoint * refactor: update logoutController to use end_session_endpoint from issuer metadata --- .../controllers/auth/LogoutController.js | 20 ++++- api/strategies/openidStrategy.js | 3 +- client/src/common/types.ts | 2 +- client/src/components/Auth/Login.tsx | 6 +- client/src/components/Auth/LoginForm.tsx | 3 +- .../components/Auth/__tests__/Login.spec.tsx | 16 ++-- .../Auth/__tests__/LoginForm.spec.tsx | 16 ++-- .../Auth/__tests__/Registration.spec.tsx | 12 ++- client/src/components/Banners/Banner.tsx | 4 +- client/src/components/Chat/Footer.tsx | 2 +- client/src/components/Chat/Header.tsx | 2 +- .../src/components/Chat/Input/AddedConvo.tsx | 2 +- .../Chat/Input/Files/AttachFileMenu.tsx | 2 +- .../Chat/Input/Files/DragDropModal.tsx | 2 +- .../components/Chat/Input/HeaderOptions.tsx | 2 +- client/src/components/Chat/Landing.tsx | 7 +- .../Chat/Menus/Endpoints/MenuItem.tsx | 2 +- .../Chat/Menus/Endpoints/MenuItems.tsx | 2 +- .../components/Chat/Menus/EndpointsMenu.tsx | 2 +- .../Chat/Menus/Models/ModelSpecsMenu.tsx | 2 +- .../Chat/Menus/Presets/EditPresetDialog.tsx | 2 +- .../Chat/Menus/Presets/PresetItems.tsx | 2 +- .../components/Chat/Messages/MessageIcon.tsx | 2 +- client/src/components/Chat/Presentation.tsx | 3 +- client/src/components/Conversations/Convo.tsx | 2 +- .../ConvoOptions/ConvoOptions.tsx | 3 +- .../components/Endpoints/EndpointSettings.tsx | 5 +- .../Input/SetKeyDialog/SetKeyDialog.tsx | 8 +- .../components/Messages/Content/Plugin.tsx | 10 +-- client/src/components/Nav/AccountSettings.tsx | 2 +- client/src/components/Nav/NewChat.tsx | 2 +- .../__tests__/PluginStoreDialog.spec.tsx | 6 +- client/src/components/Prompts/Groups/List.tsx | 2 +- client/src/components/Prompts/SharePrompt.tsx | 3 +- client/src/components/Share/ShareView.tsx | 3 +- .../SidePanel/Agents/AgentPanelSwitch.tsx | 3 +- .../SidePanel/Agents/AgentSelect.tsx | 3 +- .../SidePanel/Agents/ModelPanel.tsx | 2 +- .../SidePanel/Agents/ShareAgent.tsx | 3 +- .../SidePanel/Builder/PanelSwitch.tsx | 7 +- .../components/SidePanel/Parameters/Panel.tsx | 2 +- client/src/components/SidePanel/SidePanel.tsx | 7 +- client/src/data-provider/Auth/index.ts | 1 + client/src/data-provider/Auth/mutations.ts | 43 ++++++--- client/src/data-provider/Auth/queries.ts | 20 +++++ client/src/data-provider/Endpoints/index.ts | 1 + client/src/data-provider/Endpoints/queries.ts | 41 +++++++++ client/src/data-provider/Files/queries.ts | 4 + client/src/data-provider/Misc/index.ts | 1 + client/src/data-provider/Misc/queries.ts | 45 ++++++++++ client/src/data-provider/index.ts | 2 + client/src/data-provider/queries.ts | 6 +- client/src/hooks/AuthContext.tsx | 63 +++++++------ client/src/hooks/Config/useAppStartup.ts | 2 - .../hooks/Conversations/useDefaultConvo.ts | 7 +- .../hooks/Conversations/useGenerateConvo.ts | 20 +++-- .../src/hooks/Conversations/useGetSender.ts | 2 +- client/src/hooks/Conversations/useSearch.ts | 3 +- client/src/hooks/Input/useMentions.ts | 13 +-- client/src/hooks/Input/useRequiresKey.ts | 2 +- client/src/hooks/Input/useUserKey.ts | 9 +- client/src/hooks/SSE/useSSE.ts | 23 +++-- client/src/hooks/useNewConvo.ts | 8 +- client/src/routes/ChatRoute.tsx | 13 +-- client/src/routes/Layouts/Login.tsx | 16 ++++ client/src/routes/Layouts/Startup.tsx | 2 +- client/src/routes/Root.tsx | 3 +- client/src/store/misc.ts | 7 +- packages/data-provider/src/data-service.ts | 4 +- packages/data-provider/src/keys.ts | 2 + .../src/react-query/react-query-service.ts | 88 ------------------- packages/data-provider/src/request.ts | 4 + packages/data-provider/src/types/mutations.ts | 9 +- 73 files changed, 385 insertions(+), 270 deletions(-) create mode 100644 client/src/data-provider/Auth/queries.ts create mode 100644 client/src/data-provider/Endpoints/index.ts create mode 100644 client/src/data-provider/Endpoints/queries.ts create mode 100644 client/src/data-provider/Misc/index.ts create mode 100644 client/src/data-provider/Misc/queries.ts diff --git a/api/server/controllers/auth/LogoutController.js b/api/server/controllers/auth/LogoutController.js index 7d010f0859..ed22d73404 100644 --- a/api/server/controllers/auth/LogoutController.js +++ b/api/server/controllers/auth/LogoutController.js @@ -1,5 +1,7 @@ const cookies = require('cookie'); +const { Issuer } = require('openid-client'); const { logoutUser } = require('~/server/services/AuthService'); +const { isEnabled } = require('~/server/utils'); const { logger } = require('~/config'); const logoutController = async (req, res) => { @@ -8,7 +10,23 @@ const logoutController = async (req, res) => { const logout = await logoutUser(req, refreshToken); const { status, message } = logout; res.clearCookie('refreshToken'); - return res.status(status).send({ message }); + const response = { message }; + if ( + req.user.openidId != null && + isEnabled(process.env.OPENID_USE_END_SESSION_ENDPOINT) && + process.env.OPENID_ISSUER + ) { + const issuer = await Issuer.discover(process.env.OPENID_ISSUER); + const redirect = issuer.metadata.end_session_endpoint; + if (!redirect) { + logger.warn( + '[logoutController] end_session_endpoint not found in OpenID issuer metadata. Please verify that the issuer is correct.', + ); + } else { + response.redirect = redirect; + } + } + return res.status(status).send(response); } catch (err) { logger.error('[logoutController]', err); return res.status(500).json({ message: err.message }); diff --git a/api/strategies/openidStrategy.js b/api/strategies/openidStrategy.js index 9c2da383c9..b26b11efed 100644 --- a/api/strategies/openidStrategy.js +++ b/api/strategies/openidStrategy.js @@ -129,7 +129,8 @@ async function setupOpenId() { redirect_uris: [process.env.DOMAIN_SERVER + process.env.OPENID_CALLBACK_URL], }; if (isEnabled(process.env.OPENID_SET_FIRST_SUPPORTED_ALGORITHM)) { - clientMetadata.id_token_signed_response_alg = issuer.id_token_signing_alg_values_supported?.[0] || 'RS256'; + clientMetadata.id_token_signed_response_alg = + issuer.id_token_signing_alg_values_supported?.[0] || 'RS256'; } const client = new issuer.Client(clientMetadata); const requiredRole = process.env.OPENID_REQUIRED_ROLE; diff --git a/client/src/common/types.ts b/client/src/common/types.ts index 292d4a7304..b296dcc090 100644 --- a/client/src/common/types.ts +++ b/client/src/common/types.ts @@ -412,7 +412,7 @@ export type TAuthConfig = { }; export type IconProps = Pick & - Pick & { + Pick & { size?: number; button?: boolean; iconURL?: string; diff --git a/client/src/components/Auth/Login.tsx b/client/src/components/Auth/Login.tsx index 0e62bdea71..a332553701 100644 --- a/client/src/components/Auth/Login.tsx +++ b/client/src/components/Auth/Login.tsx @@ -13,8 +13,8 @@ function Login() { return ( <> - {error && {localize(getLoginError(error))}} - {startupConfig?.emailLoginEnabled && ( + {error != null && {localize(getLoginError(error))}} + {startupConfig?.emailLoginEnabled === true && ( )} - {startupConfig?.registrationEnabled && ( + {startupConfig?.registrationEnabled === true && (

{' '} {localize('com_auth_no_account')}{' '} diff --git a/client/src/components/Auth/LoginForm.tsx b/client/src/components/Auth/LoginForm.tsx index 0c532abe40..9f5bb46039 100644 --- a/client/src/components/Auth/LoginForm.tsx +++ b/client/src/components/Auth/LoginForm.tsx @@ -1,9 +1,8 @@ import { useForm } from 'react-hook-form'; import React, { useState, useEffect } from 'react'; -import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import type { TLoginUser, TStartupConfig } from 'librechat-data-provider'; import type { TAuthContext } from '~/common'; -import { useResendVerificationEmail } from '~/data-provider'; +import { useResendVerificationEmail, useGetStartupConfig } from '~/data-provider'; import { useLocalize } from '~/hooks'; type TLoginFormProps = { diff --git a/client/src/components/Auth/__tests__/Login.spec.tsx b/client/src/components/Auth/__tests__/Login.spec.tsx index 7eebb5779b..4395ab5bf3 100644 --- a/client/src/components/Auth/__tests__/Login.spec.tsx +++ b/client/src/components/Auth/__tests__/Login.spec.tsx @@ -1,9 +1,11 @@ import reactRouter from 'react-router-dom'; import userEvent from '@testing-library/user-event'; import { getByTestId, render, waitFor } from 'test/layout-test-utils'; -import * as mockDataProvider from 'librechat-data-provider/react-query'; import type { TStartupConfig } from 'librechat-data-provider'; -import * as authDataProvider from '~/data-provider/Auth/mutations'; +import * as endpointQueries from '~/data-provider/Endpoints/queries'; +import * as miscDataProvider from '~/data-provider/Misc/queries'; +import * as authMutations from '~/data-provider/Auth/mutations'; +import * as authQueries from '~/data-provider/Auth/queries'; import AuthLayout from '~/components/Auth/AuthLayout'; import Login from '~/components/Auth/Login'; @@ -62,23 +64,23 @@ const setup = ({ }, } = {}) => { const mockUseLoginUser = jest - .spyOn(authDataProvider, 'useLoginUserMutation') + .spyOn(authMutations, 'useLoginUserMutation') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useLoginUserReturnValue); const mockUseGetUserQuery = jest - .spyOn(mockDataProvider, 'useGetUserQuery') + .spyOn(authQueries, 'useGetUserQuery') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useGetUserQueryReturnValue); const mockUseGetStartupConfig = jest - .spyOn(mockDataProvider, 'useGetStartupConfig') + .spyOn(endpointQueries, 'useGetStartupConfig') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useGetStartupConfigReturnValue); const mockUseRefreshTokenMutation = jest - .spyOn(mockDataProvider, 'useRefreshTokenMutation') + .spyOn(authMutations, 'useRefreshTokenMutation') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useRefreshTokenMutationReturnValue); const mockUseGetBannerQuery = jest - .spyOn(mockDataProvider, 'useGetBannerQuery') + .spyOn(miscDataProvider, 'useGetBannerQuery') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useGetBannerQueryReturnValue); const mockUseOutletContext = jest.spyOn(reactRouter, 'useOutletContext').mockReturnValue({ diff --git a/client/src/components/Auth/__tests__/LoginForm.spec.tsx b/client/src/components/Auth/__tests__/LoginForm.spec.tsx index 913257a00b..7bcca21c72 100644 --- a/client/src/components/Auth/__tests__/LoginForm.spec.tsx +++ b/client/src/components/Auth/__tests__/LoginForm.spec.tsx @@ -1,8 +1,10 @@ import { render, getByTestId } from 'test/layout-test-utils'; import userEvent from '@testing-library/user-event'; -import * as mockDataProvider from 'librechat-data-provider/react-query'; import type { TStartupConfig } from 'librechat-data-provider'; -import * as authDataProvider from '~/data-provider/Auth/mutations'; +import * as endpointQueries from '~/data-provider/Endpoints/queries'; +import * as miscDataProvider from '~/data-provider/Misc/queries'; +import * as authMutations from '~/data-provider/Auth/mutations'; +import * as authQueries from '~/data-provider/Auth/queries'; import Login from '../LoginForm'; jest.mock('librechat-data-provider/react-query'); @@ -67,23 +69,23 @@ const setup = ({ }, } = {}) => { const mockUseLoginUser = jest - .spyOn(authDataProvider, 'useLoginUserMutation') + .spyOn(authMutations, 'useLoginUserMutation') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useLoginUserReturnValue); const mockUseGetUserQuery = jest - .spyOn(mockDataProvider, 'useGetUserQuery') + .spyOn(authQueries, 'useGetUserQuery') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useGetUserQueryReturnValue); const mockUseGetStartupConfig = jest - .spyOn(mockDataProvider, 'useGetStartupConfig') + .spyOn(endpointQueries, 'useGetStartupConfig') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useGetStartupConfigReturnValue); const mockUseRefreshTokenMutation = jest - .spyOn(mockDataProvider, 'useRefreshTokenMutation') + .spyOn(authMutations, 'useRefreshTokenMutation') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useRefreshTokenMutationReturnValue); const mockUseGetBannerQuery = jest - .spyOn(mockDataProvider, 'useGetBannerQuery') + .spyOn(miscDataProvider, 'useGetBannerQuery') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useGetBannerQueryReturnValue); return { diff --git a/client/src/components/Auth/__tests__/Registration.spec.tsx b/client/src/components/Auth/__tests__/Registration.spec.tsx index 0a9b6d4da9..d48723ab8b 100644 --- a/client/src/components/Auth/__tests__/Registration.spec.tsx +++ b/client/src/components/Auth/__tests__/Registration.spec.tsx @@ -3,6 +3,10 @@ import userEvent from '@testing-library/user-event'; import { render, waitFor, screen } from 'test/layout-test-utils'; import * as mockDataProvider from 'librechat-data-provider/react-query'; import type { TStartupConfig } from 'librechat-data-provider'; +import * as miscDataProvider from '~/data-provider/Misc/queries'; +import * as endpointQueries from '~/data-provider/Endpoints/queries'; +import * as authMutations from '~/data-provider/Auth/mutations'; +import * as authQueries from '~/data-provider/Auth/queries'; import Registration from '~/components/Auth/Registration'; import AuthLayout from '~/components/Auth/AuthLayout'; @@ -62,22 +66,22 @@ const setup = ({ //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useRegisterUserMutationReturnValue); const mockUseGetUserQuery = jest - .spyOn(mockDataProvider, 'useGetUserQuery') + .spyOn(authQueries, 'useGetUserQuery') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useGetUserQueryReturnValue); const mockUseGetStartupConfig = jest - .spyOn(mockDataProvider, 'useGetStartupConfig') + .spyOn(endpointQueries, 'useGetStartupConfig') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useGetStartupConfigReturnValue); const mockUseRefreshTokenMutation = jest - .spyOn(mockDataProvider, 'useRefreshTokenMutation') + .spyOn(authMutations, 'useRefreshTokenMutation') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useRefreshTokenMutationReturnValue); const mockUseOutletContext = jest.spyOn(reactRouter, 'useOutletContext').mockReturnValue({ startupConfig: useGetStartupConfigReturnValue.data, }); const mockUseGetBannerQuery = jest - .spyOn(mockDataProvider, 'useGetBannerQuery') + .spyOn(miscDataProvider, 'useGetBannerQuery') //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult .mockReturnValue(useGetBannerQueryReturnValue); const renderResult = render( diff --git a/client/src/components/Banners/Banner.tsx b/client/src/components/Banners/Banner.tsx index cf6fcae570..d4bc2e5853 100644 --- a/client/src/components/Banners/Banner.tsx +++ b/client/src/components/Banners/Banner.tsx @@ -1,8 +1,8 @@ +import { useEffect, useRef } from 'react'; import { XIcon } from 'lucide-react'; import { useRecoilState } from 'recoil'; -import { useGetBannerQuery } from 'librechat-data-provider/react-query'; +import { useGetBannerQuery } from '~/data-provider'; import store from '~/store'; -import { useEffect, useRef } from 'react'; export const Banner = ({ onHeightChange }: { onHeightChange?: (height: number) => void }) => { const { data: banner } = useGetBannerQuery(); diff --git a/client/src/components/Chat/Footer.tsx b/client/src/components/Chat/Footer.tsx index b534aee018..75470f7e73 100644 --- a/client/src/components/Chat/Footer.tsx +++ b/client/src/components/Chat/Footer.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import ReactMarkdown from 'react-markdown'; import TagManager from 'react-gtm-module'; import { Constants } from 'librechat-data-provider'; -import { useGetStartupConfig } from 'librechat-data-provider/react-query'; +import { useGetStartupConfig } from '~/data-provider'; import { useLocalize } from '~/hooks'; export default function Footer({ className }: { className?: string }) { diff --git a/client/src/components/Chat/Header.tsx b/client/src/components/Chat/Header.tsx index 4333dc393e..8a40a04d2e 100644 --- a/client/src/components/Chat/Header.tsx +++ b/client/src/components/Chat/Header.tsx @@ -1,9 +1,9 @@ import { useMemo } from 'react'; import { useOutletContext } from 'react-router-dom'; import { getConfigDefaults, PermissionTypes, Permissions } from 'librechat-data-provider'; -import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import type { ContextType } from '~/common'; import { EndpointsMenu, ModelSpecsMenu, PresetsMenu, HeaderNewChat } from './Menus'; +import { useGetStartupConfig } from '~/data-provider'; import ExportAndShareMenu from './ExportAndShareMenu'; import { useMediaQuery, useHasAccess } from '~/hooks'; import HeaderOptions from './Input/HeaderOptions'; diff --git a/client/src/components/Chat/Input/AddedConvo.tsx b/client/src/components/Chat/Input/AddedConvo.tsx index f57f079b80..96d534cf82 100644 --- a/client/src/components/Chat/Input/AddedConvo.tsx +++ b/client/src/components/Chat/Input/AddedConvo.tsx @@ -1,8 +1,8 @@ import { useMemo } from 'react'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { TConversation, TEndpointOption, TPreset } from 'librechat-data-provider'; import type { SetterOrUpdater } from 'recoil'; import useGetSender from '~/hooks/Conversations/useGetSender'; +import { useGetEndpointsQuery } from '~/data-provider'; import { EndpointIcon } from '~/components/Endpoints'; import { getPresetTitle } from '~/utils'; diff --git a/client/src/components/Chat/Input/Files/AttachFileMenu.tsx b/client/src/components/Chat/Input/Files/AttachFileMenu.tsx index c3d9809d5e..54a8a595c4 100644 --- a/client/src/components/Chat/Input/Files/AttachFileMenu.tsx +++ b/client/src/components/Chat/Input/Files/AttachFileMenu.tsx @@ -2,8 +2,8 @@ import * as Ariakit from '@ariakit/react'; import React, { useRef, useState, useMemo } from 'react'; import { FileSearch, ImageUpIcon, TerminalSquareIcon } from 'lucide-react'; import { EToolResources, EModelEndpoint } from 'librechat-data-provider'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import { FileUpload, TooltipAnchor, DropdownPopup } from '~/components/ui'; +import { useGetEndpointsQuery } from '~/data-provider'; import { AttachmentIcon } from '~/components/svg'; import { useLocalize } from '~/hooks'; import { cn } from '~/utils'; diff --git a/client/src/components/Chat/Input/Files/DragDropModal.tsx b/client/src/components/Chat/Input/Files/DragDropModal.tsx index f4b6ead5ca..b252ae1a93 100644 --- a/client/src/components/Chat/Input/Files/DragDropModal.tsx +++ b/client/src/components/Chat/Input/Files/DragDropModal.tsx @@ -1,8 +1,8 @@ import React, { useMemo } from 'react'; import { EModelEndpoint, EToolResources } from 'librechat-data-provider'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import { FileSearch, ImageUpIcon, TerminalSquareIcon } from 'lucide-react'; import OGDialogTemplate from '~/components/ui/OGDialogTemplate'; +import { useGetEndpointsQuery } from '~/data-provider'; import useLocalize from '~/hooks/useLocalize'; import { OGDialog } from '~/components/ui'; diff --git a/client/src/components/Chat/Input/HeaderOptions.tsx b/client/src/components/Chat/Input/HeaderOptions.tsx index f8095a4c87..0bd3326b53 100644 --- a/client/src/components/Chat/Input/HeaderOptions.tsx +++ b/client/src/components/Chat/Input/HeaderOptions.tsx @@ -2,13 +2,13 @@ import { useRecoilState } from 'recoil'; import { Settings2 } from 'lucide-react'; import { Root, Anchor } from '@radix-ui/react-popover'; import { useState, useEffect, useMemo } from 'react'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import { tConvoUpdateSchema, EModelEndpoint, isParamEndpoint } from 'librechat-data-provider'; import type { TPreset, TInterfaceConfig } from 'librechat-data-provider'; import { EndpointSettings, SaveAsPresetDialog, AlternativeSettings } from '~/components/Endpoints'; import { PluginStoreDialog, TooltipAnchor } from '~/components'; import { ModelSelect } from '~/components/Input/ModelSelect'; import { useSetIndexOptions, useLocalize } from '~/hooks'; +import { useGetEndpointsQuery } from '~/data-provider'; import OptionsPopover from './OptionsPopover'; import PopoverButtons from './PopoverButtons'; import { useChatContext } from '~/Providers'; diff --git a/client/src/components/Chat/Landing.tsx b/client/src/components/Chat/Landing.tsx index 8d65fba0ba..bfb1a34b69 100644 --- a/client/src/components/Chat/Landing.tsx +++ b/client/src/components/Chat/Landing.tsx @@ -1,10 +1,13 @@ import { useMemo } from 'react'; import { EModelEndpoint, Constants } from 'librechat-data-provider'; -import { useGetEndpointsQuery, useGetStartupConfig } from 'librechat-data-provider/react-query'; import type * as t from 'librechat-data-provider'; import type { ReactNode } from 'react'; import { useChatContext, useAgentsMapContext, useAssistantsMapContext } from '~/Providers'; -import { useGetAssistantDocsQuery } from '~/data-provider'; +import { + useGetAssistantDocsQuery, + useGetEndpointsQuery, + useGetStartupConfig, +} from '~/data-provider'; import ConvoIcon from '~/components/Endpoints/ConvoIcon'; import { getIconEndpoint, getEntity, cn } from '~/utils'; import { useLocalize, useSubmitMessage } from '~/hooks'; diff --git a/client/src/components/Chat/Menus/Endpoints/MenuItem.tsx b/client/src/components/Chat/Menus/Endpoints/MenuItem.tsx index 66a5d048bf..671abd0d21 100644 --- a/client/src/components/Chat/Menus/Endpoints/MenuItem.tsx +++ b/client/src/components/Chat/Menus/Endpoints/MenuItem.tsx @@ -2,12 +2,12 @@ import { useState } from 'react'; import { Settings } from 'lucide-react'; import { useRecoilValue } from 'recoil'; import { EModelEndpoint } from 'librechat-data-provider'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { TConversation } from 'librechat-data-provider'; import type { FC } from 'react'; import { cn, getConvoSwitchLogic, getEndpointField, getIconKey } from '~/utils'; import { useLocalize, useUserKey, useDefaultConvo } from '~/hooks'; import { SetKeyDialog } from '~/components/Input/SetKeyDialog'; +import { useGetEndpointsQuery } from '~/data-provider'; import { useChatContext } from '~/Providers'; import { icons } from './Icons'; import store from '~/store'; diff --git a/client/src/components/Chat/Menus/Endpoints/MenuItems.tsx b/client/src/components/Chat/Menus/Endpoints/MenuItems.tsx index aa50f274d3..28fb167f18 100644 --- a/client/src/components/Chat/Menus/Endpoints/MenuItems.tsx +++ b/client/src/components/Chat/Menus/Endpoints/MenuItems.tsx @@ -6,7 +6,7 @@ import { PermissionTypes, Permissions, } from 'librechat-data-provider'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; +import { useGetEndpointsQuery } from '~/data-provider'; import MenuSeparator from '../UI/MenuSeparator'; import { getEndpointField } from '~/utils'; import { useHasAccess } from '~/hooks'; diff --git a/client/src/components/Chat/Menus/EndpointsMenu.tsx b/client/src/components/Chat/Menus/EndpointsMenu.tsx index 4ec04a3773..ee90ddeded 100644 --- a/client/src/components/Chat/Menus/EndpointsMenu.tsx +++ b/client/src/components/Chat/Menus/EndpointsMenu.tsx @@ -1,9 +1,9 @@ import { useCallback, useRef } from 'react'; import { alternateName } from 'librechat-data-provider'; import { Content, Portal, Root } from '@radix-ui/react-popover'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { FC, KeyboardEvent } from 'react'; import { useChatContext, useAgentsMapContext, useAssistantsMapContext } from '~/Providers'; +import { useGetEndpointsQuery } from '~/data-provider'; import { mapEndpoints, getEntity } from '~/utils'; import EndpointItems from './Endpoints/MenuItems'; import useLocalize from '~/hooks/useLocalize'; diff --git a/client/src/components/Chat/Menus/Models/ModelSpecsMenu.tsx b/client/src/components/Chat/Menus/Models/ModelSpecsMenu.tsx index 4387f06aaf..400a514dd4 100644 --- a/client/src/components/Chat/Menus/Models/ModelSpecsMenu.tsx +++ b/client/src/components/Chat/Menus/Models/ModelSpecsMenu.tsx @@ -1,13 +1,13 @@ import { useRecoilValue } from 'recoil'; import { useMemo, useCallback, useRef } from 'react'; import { Content, Portal, Root } from '@radix-ui/react-popover'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import { EModelEndpoint, isAssistantsEndpoint } from 'librechat-data-provider'; import type { TModelSpec, TConversation, TEndpointsConfig } from 'librechat-data-provider'; import type { KeyboardEvent } from 'react'; import { useChatContext, useAssistantsMapContext } from '~/Providers'; import { useDefaultConvo, useNewConvo, useLocalize } from '~/hooks'; import { getConvoSwitchLogic, getModelSpecIconURL } from '~/utils'; +import { useGetEndpointsQuery } from '~/data-provider'; import MenuButton from './MenuButton'; import ModelSpecs from './ModelSpecs'; import store from '~/store'; diff --git a/client/src/components/Chat/Menus/Presets/EditPresetDialog.tsx b/client/src/components/Chat/Menus/Presets/EditPresetDialog.tsx index 95d0e6f906..0978fe462a 100644 --- a/client/src/components/Chat/Menus/Presets/EditPresetDialog.tsx +++ b/client/src/components/Chat/Menus/Presets/EditPresetDialog.tsx @@ -2,7 +2,6 @@ import { useRecoilState } from 'recoil'; import { useCallback, useEffect, useMemo } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { QueryKeys, isAgentsEndpoint } from 'librechat-data-provider'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { TModelsConfig, TEndpointsConfig } from 'librechat-data-provider'; import { cn, @@ -16,6 +15,7 @@ import { useSetIndexOptions, useLocalize, useDebouncedInput } from '~/hooks'; import PopoverButtons from '~/components/Chat/Input/PopoverButtons'; import DialogTemplate from '~/components/ui/DialogTemplate'; import { EndpointSettings } from '~/components/Endpoints'; +import { useGetEndpointsQuery } from '~/data-provider'; import { useChatContext } from '~/Providers'; import store from '~/store'; diff --git a/client/src/components/Chat/Menus/Presets/PresetItems.tsx b/client/src/components/Chat/Menus/Presets/PresetItems.tsx index d5df89a062..552b8b5407 100644 --- a/client/src/components/Chat/Menus/Presets/PresetItems.tsx +++ b/client/src/components/Chat/Menus/Presets/PresetItems.tsx @@ -1,7 +1,6 @@ import { useRecoilValue } from 'recoil'; import { Close } from '@radix-ui/react-popover'; import { Flipper, Flipped } from 'react-flip-toolkit'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { FC } from 'react'; import type { TPreset } from 'librechat-data-provider'; import { getPresetTitle, getEndpointField, getIconKey } from '~/utils'; @@ -9,6 +8,7 @@ import FileUpload from '~/components/Chat/Input/Files/FileUpload'; import { PinIcon, EditIcon, TrashIcon } from '~/components/svg'; import { Dialog, DialogTrigger, Label } from '~/components/ui'; import DialogTemplate from '~/components/ui/DialogTemplate'; +import { useGetEndpointsQuery } from '~/data-provider'; import { MenuSeparator, MenuItem } from '../UI'; import { icons } from '../Endpoints/Icons'; import { useLocalize } from '~/hooks'; diff --git a/client/src/components/Chat/Messages/MessageIcon.tsx b/client/src/components/Chat/Messages/MessageIcon.tsx index 8cb4afcaaa..8ef8ed794a 100644 --- a/client/src/components/Chat/Messages/MessageIcon.tsx +++ b/client/src/components/Chat/Messages/MessageIcon.tsx @@ -1,9 +1,9 @@ import React, { useMemo, memo } from 'react'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { Assistant, Agent } from 'librechat-data-provider'; import type { TMessageIcon } from '~/common'; import { getEndpointField, getIconEndpoint, logger } from '~/utils'; import ConvoIconURL from '~/components/Endpoints/ConvoIconURL'; +import { useGetEndpointsQuery } from '~/data-provider'; import Icon from '~/components/Endpoints/Icon'; const MessageIcon = memo( diff --git a/client/src/components/Chat/Presentation.tsx b/client/src/components/Chat/Presentation.tsx index 9ab483924b..c92f604ee1 100644 --- a/client/src/components/Chat/Presentation.tsx +++ b/client/src/components/Chat/Presentation.tsx @@ -1,10 +1,9 @@ import { useRecoilValue } from 'recoil'; import { useEffect, useMemo } from 'react'; -import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import { FileSources, LocalStorageKeys, getConfigDefaults } from 'librechat-data-provider'; import type { ExtendedFile } from '~/common'; +import { useDeleteFilesMutation, useGetStartupConfig } from '~/data-provider'; import DragDropWrapper from '~/components/Chat/Input/Files/DragDropWrapper'; -import { useDeleteFilesMutation } from '~/data-provider'; import Artifacts from '~/components/Artifacts/Artifacts'; import { SidePanel } from '~/components/SidePanel'; import { useSetFilesToDelete } from '~/hooks'; diff --git a/client/src/components/Conversations/Convo.tsx b/client/src/components/Conversations/Convo.tsx index 42d8781283..b0e6e06636 100644 --- a/client/src/components/Conversations/Convo.tsx +++ b/client/src/components/Conversations/Convo.tsx @@ -3,12 +3,12 @@ import { useRecoilValue } from 'recoil'; import { Check, X } from 'lucide-react'; import { useParams } from 'react-router-dom'; import { Constants } from 'librechat-data-provider'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react'; import type { TConversation } from 'librechat-data-provider'; import { useNavigateToConvo, useMediaQuery, useLocalize } from '~/hooks'; import { useUpdateConversationMutation } from '~/data-provider'; import EndpointIcon from '~/components/Endpoints/EndpointIcon'; +import { useGetEndpointsQuery } from '~/data-provider'; import { NotificationSeverity } from '~/common'; import { useToastContext } from '~/Providers'; import { ConvoOptions } from './ConvoOptions'; diff --git a/client/src/components/Conversations/ConvoOptions/ConvoOptions.tsx b/client/src/components/Conversations/ConvoOptions/ConvoOptions.tsx index 1b04a27d78..ea245c0bbd 100644 --- a/client/src/components/Conversations/ConvoOptions/ConvoOptions.tsx +++ b/client/src/components/Conversations/ConvoOptions/ConvoOptions.tsx @@ -1,12 +1,11 @@ import { useState, useId, useRef, memo } from 'react'; import * as Menu from '@ariakit/react/menu'; import { Ellipsis, Share2, Copy, Archive, Pen, Trash } from 'lucide-react'; -import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import type { MouseEvent } from 'react'; import type * as t from '~/common'; +import { useDuplicateConversationMutation, useGetStartupConfig } from '~/data-provider'; import { useLocalize, useArchiveHandler, useNavigateToConvo } from '~/hooks'; import { useToastContext, useChatContext } from '~/Providers'; -import { useDuplicateConversationMutation } from '~/data-provider'; import { DropdownPopup } from '~/components/ui'; import DeleteButton from './DeleteButton'; import ShareButton from './ShareButton'; diff --git a/client/src/components/Endpoints/EndpointSettings.tsx b/client/src/components/Endpoints/EndpointSettings.tsx index 5d38fd64d4..ce8be90634 100644 --- a/client/src/components/Endpoints/EndpointSettings.tsx +++ b/client/src/components/Endpoints/EndpointSettings.tsx @@ -1,9 +1,10 @@ import { useRecoilValue } from 'recoil'; import { SettingsViews, TConversation } from 'librechat-data-provider'; -import { useGetModelsQuery, useGetEndpointsQuery } from 'librechat-data-provider/react-query'; +import { useGetModelsQuery } from 'librechat-data-provider/react-query'; import type { TSettingsProps } from '~/common'; -import { getSettings } from './Settings'; +import { useGetEndpointsQuery } from '~/data-provider'; import { cn, getEndpointField } from '~/utils'; +import { getSettings } from './Settings'; import store from '~/store'; export default function Settings({ diff --git a/client/src/components/Input/SetKeyDialog/SetKeyDialog.tsx b/client/src/components/Input/SetKeyDialog/SetKeyDialog.tsx index fc371d73a3..5706265feb 100644 --- a/client/src/components/Input/SetKeyDialog/SetKeyDialog.tsx +++ b/client/src/components/Input/SetKeyDialog/SetKeyDialog.tsx @@ -1,11 +1,11 @@ import React, { useState } from 'react'; import { useForm, FormProvider } from 'react-hook-form'; import { EModelEndpoint, alternateName, isAssistantsEndpoint } from 'librechat-data-provider'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { TDialogProps } from '~/common'; import OGDialogTemplate from '~/components/ui/OGDialogTemplate'; -import { RevokeKeysButton } from '~/components/Nav'; +import { useGetEndpointsQuery } from '~/data-provider'; import { OGDialog, Dropdown } from '~/components/ui'; +import { RevokeKeysButton } from '~/components/Nav'; import { useUserKey, useLocalize } from '~/hooks'; import { useToastContext } from '~/Providers'; import CustomConfig from './CustomEndpoint'; @@ -118,7 +118,7 @@ const SetKeyDialog = ({ if (isOpenAIBase && key === 'baseURL') { return false; } - if (key === 'baseURL' && !userProvideURL) { + if (key === 'baseURL' && !(userProvideURL ?? false)) { return false; } return data[key] === ''; @@ -205,7 +205,7 @@ const SetKeyDialog = ({ leftButtons={ } diff --git a/client/src/components/Messages/Content/Plugin.tsx b/client/src/components/Messages/Content/Plugin.tsx index f0f38ebacc..b2b7f7eed8 100644 --- a/client/src/components/Messages/Content/Plugin.tsx +++ b/client/src/components/Messages/Content/Plugin.tsx @@ -1,8 +1,8 @@ import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'; import { useCallback, memo, ReactNode } from 'react'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; -import type { TResPlugin, TInput } from 'librechat-data-provider'; import { ChevronDownIcon, LucideProps } from 'lucide-react'; +import type { TResPlugin, TInput } from 'librechat-data-provider'; +import { useGetEndpointsQuery } from '~/data-provider'; import { useShareContext } from '~/Providers'; import { cn, formatJSON } from '~/utils'; import { Spinner } from '~/components'; @@ -47,7 +47,7 @@ const Plugin: React.FC = ({ plugin }) => { if (pluginKey === 'n/a' || pluginKey === 'self reflection') { return pluginKey; } - return plugins?.[pluginKey] ?? 'self reflection'; + return plugins[pluginKey] ?? 'self reflection'; }, [plugins], ); @@ -105,7 +105,7 @@ const Plugin: React.FC = ({ plugin }) => { = ({ plugin }) => { {plugin.outputs && plugin.outputs.length > 0 && ( => { +): UseMutationResult => { const queryClient = useQueryClient(); const clearStates = useClearStates(); const resetDefaultPreset = useResetRecoilState(store.defaultPreset); + const setQueriesEnabled = useSetRecoilState(store.queriesEnabled); return useMutation([MutationKeys.logoutUser], { mutationFn: () => dataService.logout(), ...(options || {}), onSuccess: (...args) => { + setQueriesEnabled(false); resetDefaultPreset(); clearStates(); queryClient.removeQueries(); @@ -26,20 +28,39 @@ export const useLogoutUserMutation = ( }); }; -export const useLoginUserMutation = (): UseMutationResult< - t.TLoginResponse, - unknown, - t.TLoginUser, - unknown -> => { +export const useLoginUserMutation = ( + options?: t.MutationOptions, +): UseMutationResult => { const queryClient = useQueryClient(); const clearStates = useClearStates(); const resetDefaultPreset = useResetRecoilState(store.defaultPreset); - return useMutation((payload: t.TLoginUser) => dataService.login(payload), { - onMutate: () => { + const setQueriesEnabled = useSetRecoilState(store.queriesEnabled); + return useMutation([MutationKeys.loginUser], { + mutationFn: (payload: t.TLoginUser) => dataService.login(payload), + ...(options || {}), + onMutate: (vars) => { resetDefaultPreset(); clearStates(); queryClient.removeQueries(); + options?.onMutate?.(vars); + }, + onSuccess: (...args) => { + setQueriesEnabled(true); + options?.onSuccess?.(...args); + }, + }); +}; + +export const useRefreshTokenMutation = ( + options?: t.MutationOptions, +): UseMutationResult => { + const queryClient = useQueryClient(); + return useMutation([MutationKeys.refreshToken], { + mutationFn: () => request.refreshToken(), + ...(options || {}), + onMutate: (vars) => { + queryClient.removeQueries(); + options?.onMutate?.(vars); }, }); }; diff --git a/client/src/data-provider/Auth/queries.ts b/client/src/data-provider/Auth/queries.ts new file mode 100644 index 0000000000..5fbbc49eb5 --- /dev/null +++ b/client/src/data-provider/Auth/queries.ts @@ -0,0 +1,20 @@ +import { useRecoilValue } from 'recoil'; +import { QueryKeys, dataService } from 'librechat-data-provider'; +import { useQuery } from '@tanstack/react-query'; +import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; +import type t from 'librechat-data-provider'; +import store from '~/store'; + +export const useGetUserQuery = ( + config?: UseQueryOptions, +): QueryObserverResult => { + const queriesEnabled = useRecoilValue(store.queriesEnabled); + return useQuery([QueryKeys.user], () => dataService.getUser(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + retry: false, + ...config, + enabled: (config?.enabled ?? true) === true && queriesEnabled, + }); +}; diff --git a/client/src/data-provider/Endpoints/index.ts b/client/src/data-provider/Endpoints/index.ts new file mode 100644 index 0000000000..3cf1ef310b --- /dev/null +++ b/client/src/data-provider/Endpoints/index.ts @@ -0,0 +1 @@ +export * from './queries'; diff --git a/client/src/data-provider/Endpoints/queries.ts b/client/src/data-provider/Endpoints/queries.ts new file mode 100644 index 0000000000..2ef26a5bd9 --- /dev/null +++ b/client/src/data-provider/Endpoints/queries.ts @@ -0,0 +1,41 @@ +import { useRecoilValue } from 'recoil'; +import { QueryKeys, dataService } from 'librechat-data-provider'; +import { useQuery } from '@tanstack/react-query'; +import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; +import type t from 'librechat-data-provider'; +import store from '~/store'; + +export const useGetEndpointsQuery = ( + config?: UseQueryOptions, +): QueryObserverResult => { + const queriesEnabled = useRecoilValue(store.queriesEnabled); + return useQuery( + [QueryKeys.endpoints], + () => dataService.getAIEndpoints(), + { + staleTime: Infinity, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + enabled: (config?.enabled ?? true) === true && queriesEnabled, + }, + ); +}; + +export const useGetStartupConfig = ( + config?: UseQueryOptions, +): QueryObserverResult => { + const queriesEnabled = useRecoilValue(store.queriesEnabled); + return useQuery( + [QueryKeys.startupConfig], + () => dataService.getStartupConfig(), + { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + enabled: (config?.enabled ?? true) === true && queriesEnabled, + }, + ); +}; diff --git a/client/src/data-provider/Files/queries.ts b/client/src/data-provider/Files/queries.ts index 4b79341475..4f5805b269 100644 --- a/client/src/data-provider/Files/queries.ts +++ b/client/src/data-provider/Files/queries.ts @@ -1,17 +1,21 @@ +import { useRecoilValue } from 'recoil'; import { QueryKeys, dataService } from 'librechat-data-provider'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; import type t from 'librechat-data-provider'; import { addFileToCache } from '~/utils'; +import store from '~/store'; export const useGetFiles = ( config?: UseQueryOptions, ): QueryObserverResult => { + const queriesEnabled = useRecoilValue(store.queriesEnabled); return useQuery([QueryKeys.files], () => dataService.getFiles(), { refetchOnWindowFocus: false, refetchOnReconnect: false, refetchOnMount: false, ...config, + enabled: (config?.enabled ?? true) === true && queriesEnabled, }); }; diff --git a/client/src/data-provider/Misc/index.ts b/client/src/data-provider/Misc/index.ts new file mode 100644 index 0000000000..3cf1ef310b --- /dev/null +++ b/client/src/data-provider/Misc/index.ts @@ -0,0 +1 @@ +export * from './queries'; diff --git a/client/src/data-provider/Misc/queries.ts b/client/src/data-provider/Misc/queries.ts new file mode 100644 index 0000000000..b5127068f4 --- /dev/null +++ b/client/src/data-provider/Misc/queries.ts @@ -0,0 +1,45 @@ +import { useRecoilValue } from 'recoil'; +import { QueryKeys, dataService } from 'librechat-data-provider'; +import { useQuery } from '@tanstack/react-query'; +import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; +import type t from 'librechat-data-provider'; +import store from '~/store'; + +export const useGetBannerQuery = ( + config?: UseQueryOptions, +): QueryObserverResult => { + const queriesEnabled = useRecoilValue(store.queriesEnabled); + return useQuery([QueryKeys.banner], () => dataService.getBanner(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + enabled: (config?.enabled ?? true) === true && queriesEnabled, + }); +}; + +export const useGetUserBalance = ( + config?: UseQueryOptions, +): QueryObserverResult => { + const queriesEnabled = useRecoilValue(store.queriesEnabled); + return useQuery([QueryKeys.balance], () => dataService.getUserBalance(), { + refetchOnWindowFocus: true, + refetchOnReconnect: true, + refetchOnMount: true, + ...config, + enabled: (config?.enabled ?? true) === true && queriesEnabled, + }); +}; + +export const useGetSearchEnabledQuery = ( + config?: UseQueryOptions, +): QueryObserverResult => { + const queriesEnabled = useRecoilValue(store.queriesEnabled); + return useQuery([QueryKeys.searchEnabled], () => dataService.getSearchEnabled(), { + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, + ...config, + enabled: (config?.enabled ?? true) === true && queriesEnabled, + }); +}; diff --git a/client/src/data-provider/index.ts b/client/src/data-provider/index.ts index c6d832687f..0c476fd11c 100644 --- a/client/src/data-provider/index.ts +++ b/client/src/data-provider/index.ts @@ -1,7 +1,9 @@ export * from './Auth'; export * from './Agents'; +export * from './Endpoints'; export * from './Files'; export * from './Messages'; +export * from './Misc'; export * from './Tools'; export * from './connection'; export * from './mutations'; diff --git a/client/src/data-provider/queries.ts b/client/src/data-provider/queries.ts index f4efe03f3e..631a3b0f0d 100644 --- a/client/src/data-provider/queries.ts +++ b/client/src/data-provider/queries.ts @@ -5,6 +5,7 @@ import { defaultOrderQuery, defaultAssistantsVersion, } from 'librechat-data-provider'; +import { useRecoilValue } from 'recoil'; import { useQuery, useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; import type { UseInfiniteQueryOptions, @@ -28,6 +29,7 @@ import type { SharedLinksResponse, } from 'librechat-data-provider'; import { findPageForConversation } from '~/utils'; +import store from '~/store'; export const useGetPresetsQuery = ( config?: UseQueryOptions, @@ -115,6 +117,7 @@ export const useConversationsInfiniteQuery = ( params?: ConversationListParams, config?: UseInfiniteQueryOptions, ) => { + const queriesEnabled = useRecoilValue(store.queriesEnabled); return useInfiniteQuery( params?.isArchived === true ? [QueryKeys.archivedConversations] : [QueryKeys.allConversations], ({ pageParam = '' }) => @@ -135,6 +138,7 @@ export const useConversationsInfiniteQuery = ( refetchOnReconnect: false, refetchOnMount: false, ...config, + enabled: (config?.enabled ?? true) === true && queriesEnabled, }, ); }; @@ -156,7 +160,7 @@ export const useSharedLinksQuery = ( sortBy, sortDirection, }), - getNextPageParam: (lastPage) => lastPage?.nextCursor ?? undefined, + getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined, keepPreviousData: true, staleTime: 5 * 60 * 1000, // 5 minutes cacheTime: 30 * 60 * 1000, // 30 minutes diff --git a/client/src/hooks/AuthContext.tsx b/client/src/hooks/AuthContext.tsx index 9c94a0bcee..3a680f30b8 100644 --- a/client/src/hooks/AuthContext.tsx +++ b/client/src/hooks/AuthContext.tsx @@ -7,12 +7,17 @@ import { useCallback, createContext, } from 'react'; -import { useRecoilState } from 'recoil'; import { useNavigate } from 'react-router-dom'; +import { useRecoilState, useRecoilValue } from 'recoil'; import { setTokenHeader, SystemRoles } from 'librechat-data-provider'; -import { useGetUserQuery, useRefreshTokenMutation } from 'librechat-data-provider/react-query'; -import type { TLoginResponse, TLoginUser } from 'librechat-data-provider'; -import { useLoginUserMutation, useLogoutUserMutation, useGetRole } from '~/data-provider'; +import type * as t from 'librechat-data-provider'; +import { + useGetRole, + useGetUserQuery, + useLoginUserMutation, + useLogoutUserMutation, + useRefreshTokenMutation, +} from '~/data-provider'; import { TAuthConfig, TUserContext, TAuthContext, TResError } from '~/common'; import useTimeout from './useTimeout'; import store from '~/store'; @@ -42,14 +47,20 @@ const AuthContextProvider = ({ const setUserContext = useCallback( (userContext: TUserContext) => { const { token, isAuthenticated, user, redirect } = userContext; - if (user) { - setUser(user); - } + setUser(user); setToken(token); //@ts-ignore - ok for token to be undefined initially setTokenHeader(token); setIsAuthenticated(isAuthenticated); - if (redirect != null && redirect) { + if (redirect == null) { + return; + } + if (redirect.startsWith('http://') || redirect.startsWith('https://')) { + // For external links, use window.location + window.location.href = redirect; + // Or if you want to open in a new tab: + // window.open(redirect, '_blank'); + } else { navigate(redirect, { replace: true }); } }, @@ -57,14 +68,25 @@ const AuthContextProvider = ({ ); const doSetError = useTimeout({ callback: (error) => setError(error as string | undefined) }); - const loginUser = useLoginUserMutation(); + const loginUser = useLoginUserMutation({ + onSuccess: (data: t.TLoginResponse) => { + const { user, token } = data; + setError(undefined); + setUserContext({ token, isAuthenticated: true, user, redirect: '/c/new' }); + }, + onError: (error: TResError | unknown) => { + const resError = error as TResError; + doSetError(resError.message); + navigate('/login', { replace: true }); + }, + }); const logoutUser = useLogoutUserMutation({ - onSuccess: () => { + onSuccess: (data) => { setUserContext({ token: undefined, isAuthenticated: false, user: undefined, - redirect: '/login', + redirect: data.redirect ?? '/login', }); }, onError: (error) => { @@ -77,24 +99,13 @@ const AuthContextProvider = ({ }); }, }); + const refreshToken = useRefreshTokenMutation(); const logout = useCallback(() => logoutUser.mutate(undefined), [logoutUser]); const userQuery = useGetUserQuery({ enabled: !!(token ?? '') }); - const refreshToken = useRefreshTokenMutation(); - const login = (data: TLoginUser) => { - loginUser.mutate(data, { - onSuccess: (data: TLoginResponse) => { - const { user, token } = data; - setError(undefined); - setUserContext({ token, isAuthenticated: true, user, redirect: '/c/new' }); - }, - onError: (error: TResError | unknown) => { - const resError = error as TResError; - doSetError(resError.message); - navigate('/login', { replace: true }); - }, - }); + const login = (data: t.TLoginUser) => { + loginUser.mutate(data); }; const silentRefresh = useCallback(() => { @@ -103,7 +114,7 @@ const AuthContextProvider = ({ return; } refreshToken.mutate(undefined, { - onSuccess: (data: TLoginResponse | undefined) => { + onSuccess: (data: t.TRefreshTokenResponse | undefined) => { const { user, token = '' } = data ?? {}; if (token) { setUserContext({ token, isAuthenticated: true, user }); diff --git a/client/src/hooks/Config/useAppStartup.ts b/client/src/hooks/Config/useAppStartup.ts index a806104d13..82f888fb4a 100644 --- a/client/src/hooks/Config/useAppStartup.ts +++ b/client/src/hooks/Config/useAppStartup.ts @@ -5,7 +5,6 @@ import { LocalStorageKeys } from 'librechat-data-provider'; import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query'; import type { TStartupConfig, TPlugin, TUser } from 'librechat-data-provider'; import { mapPlugins, selectPlugins, processPlugins } from '~/utils'; -import useConfigOverride from './useConfigOverride'; import store from '~/store'; const pluginStore: TPlugin = { @@ -25,7 +24,6 @@ export default function useAppStartup({ startupConfig?: TStartupConfig; user?: TUser; }) { - useConfigOverride(); const setAvailableTools = useSetRecoilState(store.availableTools); const [defaultPreset, setDefaultPreset] = useRecoilState(store.defaultPreset); const { data: allPlugins } = useAvailablePluginsQuery({ diff --git a/client/src/hooks/Conversations/useDefaultConvo.ts b/client/src/hooks/Conversations/useDefaultConvo.ts index b5833cb36b..777e2c4b74 100644 --- a/client/src/hooks/Conversations/useDefaultConvo.ts +++ b/client/src/hooks/Conversations/useDefaultConvo.ts @@ -1,11 +1,12 @@ -import { useGetEndpointsQuery, useGetModelsQuery } from 'librechat-data-provider/react-query'; +import { useGetModelsQuery } from 'librechat-data-provider/react-query'; import type { - TConversation, - TPreset, TEndpointsConfig, TModelsConfig, + TConversation, + TPreset, } from 'librechat-data-provider'; import { getDefaultEndpoint, buildDefaultConvo } from '~/utils'; +import { useGetEndpointsQuery } from '~/data-provider'; type TDefaultConvo = { conversation: Partial; preset?: Partial | null }; diff --git a/client/src/hooks/Conversations/useGenerateConvo.ts b/client/src/hooks/Conversations/useGenerateConvo.ts index a5f2c933bd..8f545161a2 100644 --- a/client/src/hooks/Conversations/useGenerateConvo.ts +++ b/client/src/hooks/Conversations/useGenerateConvo.ts @@ -1,17 +1,19 @@ import { useRecoilValue } from 'recoil'; import { useCallback, useRef, useEffect } from 'react'; +import { useGetModelsQuery } from 'librechat-data-provider/react-query'; import { LocalStorageKeys, isAssistantsEndpoint } from 'librechat-data-provider'; -import { useGetModelsQuery, useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { TPreset, TModelsConfig, TConversation, TEndpointsConfig, + EModelEndpoint, } from 'librechat-data-provider'; import type { SetterOrUpdater } from 'recoil'; import type { AssistantListItem } from '~/common'; import { getEndpointField, buildDefaultConvo, getDefaultEndpoint } from '~/utils'; import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap'; +import { useGetEndpointsQuery } from '~/data-provider'; import { mainTextareaId } from '~/common'; import store from '~/store'; @@ -32,7 +34,7 @@ const useGenerateConvo = ({ const rootConvo = useRecoilValue(store.conversationByKeySelector(rootIndex)); useEffect(() => { - if (rootConvo?.conversationId && setConversation) { + if (rootConvo?.conversationId != null && setConversation) { setConversation((prevState) => { if (!prevState) { return prevState; @@ -85,11 +87,11 @@ const useGenerateConvo = ({ } const isAssistantEndpoint = isAssistantsEndpoint(defaultEndpoint); - const assistants: AssistantListItem[] = assistantsListMap[defaultEndpoint] ?? []; + const assistants: AssistantListItem[] = assistantsListMap[defaultEndpoint ?? ''] ?? []; if ( conversation.assistant_id && - !assistantsListMap[defaultEndpoint]?.[conversation.assistant_id] + !assistantsListMap[defaultEndpoint ?? '']?.[conversation.assistant_id] ) { conversation.assistant_id = undefined; } @@ -101,7 +103,7 @@ const useGenerateConvo = ({ } if ( - conversation.assistant_id && + conversation.assistant_id != null && isAssistantEndpoint && conversation.conversationId === 'new' ) { @@ -109,19 +111,19 @@ const useGenerateConvo = ({ conversation.model = assistant?.model; } - if (conversation.assistant_id && !isAssistantEndpoint) { + if (conversation.assistant_id != null && !isAssistantEndpoint) { conversation.assistant_id = undefined; } - const models = modelsConfig?.[defaultEndpoint] ?? []; + const models = modelsConfig?.[defaultEndpoint ?? ''] ?? []; conversation = buildDefaultConvo({ conversation, lastConversationSetup: preset as TConversation, - endpoint: defaultEndpoint, + endpoint: defaultEndpoint ?? ('' as EModelEndpoint), models, }); - if (preset?.title) { + if (preset?.title != null && preset.title !== '') { conversation.title = preset.title; } diff --git a/client/src/hooks/Conversations/useGetSender.ts b/client/src/hooks/Conversations/useGetSender.ts index 5b444e614d..cced4ca4fe 100644 --- a/client/src/hooks/Conversations/useGetSender.ts +++ b/client/src/hooks/Conversations/useGetSender.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import { getResponseSender } from 'librechat-data-provider'; -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import type { TEndpointOption, TEndpointsConfig } from 'librechat-data-provider'; +import { useGetEndpointsQuery } from '~/data-provider'; export default function useGetSender() { const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery(); diff --git a/client/src/hooks/Conversations/useSearch.ts b/client/src/hooks/Conversations/useSearch.ts index 75b15148a8..753f9d9822 100644 --- a/client/src/hooks/Conversations/useSearch.ts +++ b/client/src/hooks/Conversations/useSearch.ts @@ -1,10 +1,9 @@ import { useEffect, useState, useCallback } from 'react'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useNavigate, useLocation } from 'react-router-dom'; -import { useGetSearchEnabledQuery } from 'librechat-data-provider/react-query'; import type { UseInfiniteQueryResult } from '@tanstack/react-query'; import type { ConversationListResponse } from 'librechat-data-provider'; -import { useSearchInfiniteQuery } from '~/data-provider'; +import { useSearchInfiniteQuery, useGetSearchEnabledQuery } from '~/data-provider'; import useNewConvo from '~/hooks/useNewConvo'; import store from '~/store'; diff --git a/client/src/hooks/Input/useMentions.ts b/client/src/hooks/Input/useMentions.ts index ce5fde1de9..289c7979f9 100644 --- a/client/src/hooks/Input/useMentions.ts +++ b/client/src/hooks/Input/useMentions.ts @@ -1,9 +1,5 @@ import { useMemo } from 'react'; -import { - useGetModelsQuery, - useGetStartupConfig, - useGetEndpointsQuery, -} from 'librechat-data-provider/react-query'; +import { useGetModelsQuery } from 'librechat-data-provider/react-query'; import { alternateName, EModelEndpoint, @@ -13,8 +9,13 @@ import { } from 'librechat-data-provider'; import type { TAssistantsMap, TEndpointsConfig } from 'librechat-data-provider'; import type { MentionOption } from '~/common'; +import { + useGetPresetsQuery, + useGetEndpointsQuery, + useListAgentsQuery, + useGetStartupConfig, +} from '~/data-provider'; import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap'; -import { useGetPresetsQuery, useListAgentsQuery } from '~/data-provider'; import { mapEndpoints, getPresetTitle } from '~/utils'; import { EndpointIcon } from '~/components/Endpoints'; diff --git a/client/src/hooks/Input/useRequiresKey.ts b/client/src/hooks/Input/useRequiresKey.ts index 066aa741e8..9c4b248d11 100644 --- a/client/src/hooks/Input/useRequiresKey.ts +++ b/client/src/hooks/Input/useRequiresKey.ts @@ -1,5 +1,5 @@ -import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import { useChatContext } from '~/Providers/ChatContext'; +import { useGetEndpointsQuery } from '~/data-provider'; import { getEndpointField } from '~/utils'; import useUserKey from './useUserKey'; diff --git a/client/src/hooks/Input/useUserKey.ts b/client/src/hooks/Input/useUserKey.ts index 79d374578a..124fdabc93 100644 --- a/client/src/hooks/Input/useUserKey.ts +++ b/client/src/hooks/Input/useUserKey.ts @@ -1,10 +1,7 @@ import { useMemo, useCallback } from 'react'; import { EModelEndpoint } from 'librechat-data-provider'; -import { - useUserKeyQuery, - useGetEndpointsQuery, - useUpdateUserKeysMutation, -} from 'librechat-data-provider/react-query'; +import { useUserKeyQuery, useUpdateUserKeysMutation } from 'librechat-data-provider/react-query'; +import { useGetEndpointsQuery } from '~/data-provider'; const useUserKey = (endpoint: string) => { const { data: endpointsConfig } = useGetEndpointsQuery(); @@ -24,7 +21,7 @@ const useUserKey = (endpoint: string) => { const getExpiry = useCallback(() => { if (checkUserKey.data) { - return checkUserKey.data?.expiresAt || 'never'; + return checkUserKey.data.expiresAt || 'never'; } }, [checkUserKey.data]); diff --git a/client/src/hooks/SSE/useSSE.ts b/client/src/hooks/SSE/useSSE.ts index 8b81247d5a..a52928caad 100644 --- a/client/src/hooks/SSE/useSSE.ts +++ b/client/src/hooks/SSE/useSSE.ts @@ -1,23 +1,22 @@ -import type { EventSubmission, TMessage, TPayload, TSubmission } from 'librechat-data-provider'; +import { useEffect, useState } from 'react'; +import { v4 } from 'uuid'; +import { SSE } from 'sse.js'; +import { useSetRecoilState } from 'recoil'; import { + request, /* @ts-ignore */ createPayload, isAgentsEndpoint, - isAssistantsEndpoint, removeNullishValues, - request, + isAssistantsEndpoint, } from 'librechat-data-provider'; -import { useGetStartupConfig, useGetUserBalance } from 'librechat-data-provider/react-query'; -import { useEffect, useState } from 'react'; -import { useSetRecoilState } from 'recoil'; -import { SSE } from 'sse.js'; -import { v4 } from 'uuid'; -import type { TResData } from '~/common'; -import { useGenTitleMutation } from '~/data-provider'; -import { useAuthContext } from '~/hooks/AuthContext'; -import store from '~/store'; +import type { EventSubmission, TMessage, TPayload, TSubmission } from 'librechat-data-provider'; import type { EventHandlerParams } from './useEventHandlers'; +import type { TResData } from '~/common'; +import { useGenTitleMutation, useGetStartupConfig, useGetUserBalance } from '~/data-provider'; +import { useAuthContext } from '~/hooks/AuthContext'; import useEventHandlers from './useEventHandlers'; +import store from '~/store'; type ChatHelpers = Pick< EventHandlerParams, diff --git a/client/src/hooks/useNewConvo.ts b/client/src/hooks/useNewConvo.ts index de8b902fbf..7697bd80b3 100644 --- a/client/src/hooks/useNewConvo.ts +++ b/client/src/hooks/useNewConvo.ts @@ -1,9 +1,5 @@ import { useCallback, useRef } from 'react'; -import { - useGetModelsQuery, - useGetStartupConfig, - useGetEndpointsQuery, -} from 'librechat-data-provider/react-query'; +import { useGetModelsQuery } from 'librechat-data-provider/react-query'; import { useNavigate } from 'react-router-dom'; import { Constants, @@ -30,8 +26,8 @@ import { getModelSpecIconURL, updateLastSelectedModel, } from '~/utils'; +import { useDeleteFilesMutation, useGetEndpointsQuery, useGetStartupConfig } from '~/data-provider'; import useAssistantListMap from './Assistants/useAssistantListMap'; -import { useDeleteFilesMutation } from '~/data-provider'; import { usePauseGlobalAudio } from './Audio'; import { mainTextareaId } from '~/common'; import store from '~/store'; diff --git a/client/src/routes/ChatRoute.tsx b/client/src/routes/ChatRoute.tsx index 81ff3dbd51..f6190e232e 100644 --- a/client/src/routes/ChatRoute.tsx +++ b/client/src/routes/ChatRoute.tsx @@ -1,14 +1,15 @@ import { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import { Constants, EModelEndpoint } from 'librechat-data-provider'; -import { - useGetModelsQuery, - useGetStartupConfig, - useGetEndpointsQuery, -} from 'librechat-data-provider/react-query'; +import { useGetModelsQuery } from 'librechat-data-provider/react-query'; import type { TPreset } from 'librechat-data-provider'; +import { + useGetConvoIdQuery, + useHealthCheck, + useGetEndpointsQuery, + useGetStartupConfig, +} from '~/data-provider'; import { useNewConvo, useAppStartup, useAssistantListMap } from '~/hooks'; -import { useGetConvoIdQuery, useHealthCheck } from '~/data-provider'; import { getDefaultModelSpec, getModelSpecIconURL } from '~/utils'; import { ToolCallsMapProvider } from '~/Providers'; import ChatView from '~/components/Chat/ChatView'; diff --git a/client/src/routes/Layouts/Login.tsx b/client/src/routes/Layouts/Login.tsx index 0ac4269df4..838a71e8f8 100644 --- a/client/src/routes/Layouts/Login.tsx +++ b/client/src/routes/Layouts/Login.tsx @@ -1,7 +1,23 @@ +import { useEffect } from 'react'; +import { useRecoilState } from 'recoil'; import { useAuthContext } from '~/hooks/AuthContext'; import StartupLayout from './Startup'; +import store from '~/store'; export default function LoginLayout() { const { isAuthenticated } = useAuthContext(); + const [queriesEnabled, setQueriesEnabled] = useRecoilState(store.queriesEnabled); + useEffect(() => { + if (queriesEnabled) { + return; + } + const timeout: NodeJS.Timeout = setTimeout(() => { + setQueriesEnabled(true); + }, 500); + + return () => { + clearTimeout(timeout); + }; + }, [queriesEnabled, setQueriesEnabled]); return ; } diff --git a/client/src/routes/Layouts/Startup.tsx b/client/src/routes/Layouts/Startup.tsx index 5e8e431f8b..f65566737a 100644 --- a/client/src/routes/Layouts/Startup.tsx +++ b/client/src/routes/Layouts/Startup.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { Outlet, useNavigate, useLocation } from 'react-router-dom'; -import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import type { TStartupConfig } from 'librechat-data-provider'; +import { useGetStartupConfig } from '~/data-provider'; import AuthLayout from '~/components/Auth/AuthLayout'; import { useLocalize } from '~/hooks'; diff --git a/client/src/routes/Root.tsx b/client/src/routes/Root.tsx index 242825b45b..a7d999ae45 100644 --- a/client/src/routes/Root.tsx +++ b/client/src/routes/Root.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from 'react'; import { Outlet, useNavigate } from 'react-router-dom'; -import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import type { ContextType } from '~/common'; import { AgentsMapContext, @@ -11,7 +10,7 @@ import { } from '~/Providers'; import { useAuthContext, useAssistantsMap, useAgentsMap, useFileMap, useSearch } from '~/hooks'; import TermsAndConditionsModal from '~/components/ui/TermsAndConditionsModal'; -import { useUserTermsQuery } from '~/data-provider'; +import { useUserTermsQuery, useGetStartupConfig } from '~/data-provider'; import { Nav, MobileNav } from '~/components/Nav'; import { Banner } from '~/components/Banners'; diff --git a/client/src/store/misc.ts b/client/src/store/misc.ts index 0f158ecb3b..11fd7915e5 100644 --- a/client/src/store/misc.ts +++ b/client/src/store/misc.ts @@ -9,4 +9,9 @@ const messageAttachmentsMap = atom>({ default: {}, }); -export default { hideBannerHint, messageAttachmentsMap }; +const queriesEnabled = atom({ + key: 'queriesEnabled', + default: true, +}); + +export default { hideBannerHint, messageAttachmentsMap, queriesEnabled }; diff --git a/packages/data-provider/src/data-service.ts b/packages/data-provider/src/data-service.ts index 0eadd3b725..5af00fdcb9 100644 --- a/packages/data-provider/src/data-service.ts +++ b/packages/data-provider/src/data-service.ts @@ -133,11 +133,11 @@ export const updateTokenCount = (text: string) => { return request.post(endpoints.tokenizer(), { arg: text }); }; -export const login = (payload: t.TLoginUser) => { +export const login = (payload: t.TLoginUser): Promise => { return request.post(endpoints.login(), payload); }; -export const logout = () => { +export const logout = (): Promise => { return request.post(endpoints.logout()); }; diff --git a/packages/data-provider/src/keys.ts b/packages/data-provider/src/keys.ts index 12ee8b3681..c1e0c24557 100644 --- a/packages/data-provider/src/keys.ts +++ b/packages/data-provider/src/keys.ts @@ -53,7 +53,9 @@ export enum MutationKeys { fileDelete = 'fileDelete', updatePreset = 'updatePreset', deletePreset = 'deletePreset', + loginUser = 'loginUser', logoutUser = 'logoutUser', + refreshToken = 'refreshToken', avatarUpload = 'avatarUpload', speechToText = 'speechToText', textToSpeech = 'textToSpeech', diff --git a/packages/data-provider/src/react-query/react-query-service.ts b/packages/data-provider/src/react-query/react-query-service.ts index 86eab7ddc6..03a37d99a7 100644 --- a/packages/data-provider/src/react-query/react-query-service.ts +++ b/packages/data-provider/src/react-query/react-query-service.ts @@ -5,12 +5,10 @@ import type { QueryObserverResult, } from '@tanstack/react-query'; import { initialModelsConfig } from '../config'; -import type { TStartupConfig } from '../config'; import { defaultOrderQuery } from '../types/assistants'; import * as dataService from '../data-service'; import * as m from '../types/mutations'; import { QueryKeys } from '../keys'; -import request from '../request'; import * as s from '../schemas'; import * as t from '../types'; @@ -31,18 +29,6 @@ export const useAbortRequestWithMessage = (): UseMutationResult< ); }; -export const useGetUserQuery = ( - config?: UseQueryOptions, -): QueryObserverResult => { - return useQuery([QueryKeys.user], () => dataService.getUser(), { - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchOnMount: false, - retry: false, - ...config, - }); -}; - export const useGetMessagesByConvoId = ( id: string, config?: UseQueryOptions, @@ -98,17 +84,6 @@ export const useGetSharedLinkQuery = ( ); }; -export const useGetUserBalance = ( - config?: UseQueryOptions, -): QueryObserverResult => { - return useQuery([QueryKeys.balance], () => dataService.getUserBalance(), { - refetchOnWindowFocus: true, - refetchOnReconnect: true, - refetchOnMount: true, - ...config, - }); -}; - export const useGetConversationByIdQuery = ( id: string, config?: UseQueryOptions, @@ -226,33 +201,6 @@ export const useRevokeAllUserKeysMutation = (): UseMutationResult => { }); }; -export const useGetSearchEnabledQuery = ( - config?: UseQueryOptions, -): QueryObserverResult => { - return useQuery([QueryKeys.searchEnabled], () => dataService.getSearchEnabled(), { - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchOnMount: false, - ...config, - }); -}; - -export const useGetEndpointsQuery = ( - config?: UseQueryOptions, -): QueryObserverResult => { - return useQuery( - [QueryKeys.endpoints], - () => dataService.getAIEndpoints(), - { - staleTime: Infinity, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchOnMount: false, - ...config, - }, - ); -}; - export const useGetModelsQuery = ( config?: UseQueryOptions, ): QueryObserverResult => { @@ -343,20 +291,6 @@ export const useRegisterUserMutation = ( ); }; -export const useRefreshTokenMutation = (): UseMutationResult< - t.TRefreshTokenResponse | undefined, - unknown, - unknown, - unknown -> => { - const queryClient = useQueryClient(); - return useMutation(() => request.refreshToken(), { - onMutate: () => { - queryClient.removeQueries(); - }, - }); -}; - export const useUserKeyQuery = ( name: string, config?: UseQueryOptions, @@ -428,17 +362,6 @@ export const useUpdateUserPluginsMutation = ( }); }; -export const useGetStartupConfig = ( - config?: UseQueryOptions, -): QueryObserverResult => { - return useQuery([QueryKeys.startupConfig], () => dataService.getStartupConfig(), { - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchOnMount: false, - ...config, - }); -}; - export const useGetCustomConfigSpeechQuery = ( config?: UseQueryOptions, ): QueryObserverResult => { @@ -453,14 +376,3 @@ export const useGetCustomConfigSpeechQuery = ( }, ); }; - -export const useGetBannerQuery = ( - config?: UseQueryOptions, -): QueryObserverResult => { - return useQuery([QueryKeys.banner], () => dataService.getBanner(), { - refetchOnWindowFocus: false, - refetchOnReconnect: false, - refetchOnMount: false, - ...config, - }); -}; diff --git a/packages/data-provider/src/request.ts b/packages/data-provider/src/request.ts index 239e8e8954..740e9cbe6c 100644 --- a/packages/data-provider/src/request.ts +++ b/packages/data-provider/src/request.ts @@ -91,6 +91,10 @@ axios.interceptors.response.use( return Promise.reject(error); } + if (originalRequest.url?.includes('/api/auth/logout') === true) { + return Promise.reject(error); + } + if (error.response.status === 401 && !originalRequest._retry) { console.warn('401 error, refreshing token'); originalRequest._retry = true; diff --git a/packages/data-provider/src/types/mutations.ts b/packages/data-provider/src/types/mutations.ts index 754bed65ec..de04663b95 100644 --- a/packages/data-provider/src/types/mutations.ts +++ b/packages/data-provider/src/types/mutations.ts @@ -49,8 +49,6 @@ export type UpdatePresetOptions = MutationOptions; export type DeletePresetOptions = MutationOptions; -export type LogoutOptions = MutationOptions; - /* Assistant mutations */ export type AssistantAvatarVariables = { @@ -331,3 +329,10 @@ export type EditArtifactOptions = MutationOptions< unknown, Error >; + +export type TLogoutResponse = { + message: string; + redirect?: string; +}; + +export type LogoutOptions = MutationOptions;