🛂 feat: OpenID Logout Redirect to end_session_endpoint (#5626)

* 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
This commit is contained in:
Danny Avila 2025-02-03 10:53:04 -05:00 committed by GitHub
parent d93f5c9061
commit 45dd2b262f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
73 changed files with 385 additions and 270 deletions

View file

@ -1,5 +1,7 @@
const cookies = require('cookie'); const cookies = require('cookie');
const { Issuer } = require('openid-client');
const { logoutUser } = require('~/server/services/AuthService'); const { logoutUser } = require('~/server/services/AuthService');
const { isEnabled } = require('~/server/utils');
const { logger } = require('~/config'); const { logger } = require('~/config');
const logoutController = async (req, res) => { const logoutController = async (req, res) => {
@ -8,7 +10,23 @@ const logoutController = async (req, res) => {
const logout = await logoutUser(req, refreshToken); const logout = await logoutUser(req, refreshToken);
const { status, message } = logout; const { status, message } = logout;
res.clearCookie('refreshToken'); 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) { } catch (err) {
logger.error('[logoutController]', err); logger.error('[logoutController]', err);
return res.status(500).json({ message: err.message }); return res.status(500).json({ message: err.message });

View file

@ -129,7 +129,8 @@ async function setupOpenId() {
redirect_uris: [process.env.DOMAIN_SERVER + process.env.OPENID_CALLBACK_URL], redirect_uris: [process.env.DOMAIN_SERVER + process.env.OPENID_CALLBACK_URL],
}; };
if (isEnabled(process.env.OPENID_SET_FIRST_SUPPORTED_ALGORITHM)) { 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 client = new issuer.Client(clientMetadata);
const requiredRole = process.env.OPENID_REQUIRED_ROLE; const requiredRole = process.env.OPENID_REQUIRED_ROLE;

View file

@ -412,7 +412,7 @@ export type TAuthConfig = {
}; };
export type IconProps = Pick<t.TMessage, 'isCreatedByUser' | 'model'> & export type IconProps = Pick<t.TMessage, 'isCreatedByUser' | 'model'> &
Pick<t.TConversation, 'chatGptLabel' | 'modelLabel' | 'jailbreak'> & { Pick<t.TConversation, 'chatGptLabel' | 'modelLabel'> & {
size?: number; size?: number;
button?: boolean; button?: boolean;
iconURL?: string; iconURL?: string;

View file

@ -13,8 +13,8 @@ function Login() {
return ( return (
<> <>
{error && <ErrorMessage>{localize(getLoginError(error))}</ErrorMessage>} {error != null && <ErrorMessage>{localize(getLoginError(error))}</ErrorMessage>}
{startupConfig?.emailLoginEnabled && ( {startupConfig?.emailLoginEnabled === true && (
<LoginForm <LoginForm
onSubmit={login} onSubmit={login}
startupConfig={startupConfig} startupConfig={startupConfig}
@ -22,7 +22,7 @@ function Login() {
setError={setError} setError={setError}
/> />
)} )}
{startupConfig?.registrationEnabled && ( {startupConfig?.registrationEnabled === true && (
<p className="my-4 text-center text-sm font-light text-gray-700 dark:text-white"> <p className="my-4 text-center text-sm font-light text-gray-700 dark:text-white">
{' '} {' '}
{localize('com_auth_no_account')}{' '} {localize('com_auth_no_account')}{' '}

View file

@ -1,9 +1,8 @@
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import type { TLoginUser, TStartupConfig } from 'librechat-data-provider'; import type { TLoginUser, TStartupConfig } from 'librechat-data-provider';
import type { TAuthContext } from '~/common'; import type { TAuthContext } from '~/common';
import { useResendVerificationEmail } from '~/data-provider'; import { useResendVerificationEmail, useGetStartupConfig } from '~/data-provider';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
type TLoginFormProps = { type TLoginFormProps = {

View file

@ -1,9 +1,11 @@
import reactRouter from 'react-router-dom'; import reactRouter from 'react-router-dom';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { getByTestId, render, waitFor } from 'test/layout-test-utils'; 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 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 AuthLayout from '~/components/Auth/AuthLayout';
import Login from '~/components/Auth/Login'; import Login from '~/components/Auth/Login';
@ -62,23 +64,23 @@ const setup = ({
}, },
} = {}) => { } = {}) => {
const mockUseLoginUser = jest const mockUseLoginUser = jest
.spyOn(authDataProvider, 'useLoginUserMutation') .spyOn(authMutations, 'useLoginUserMutation')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useLoginUserReturnValue); .mockReturnValue(useLoginUserReturnValue);
const mockUseGetUserQuery = jest const mockUseGetUserQuery = jest
.spyOn(mockDataProvider, 'useGetUserQuery') .spyOn(authQueries, 'useGetUserQuery')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetUserQueryReturnValue); .mockReturnValue(useGetUserQueryReturnValue);
const mockUseGetStartupConfig = jest const mockUseGetStartupConfig = jest
.spyOn(mockDataProvider, 'useGetStartupConfig') .spyOn(endpointQueries, 'useGetStartupConfig')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetStartupConfigReturnValue); .mockReturnValue(useGetStartupConfigReturnValue);
const mockUseRefreshTokenMutation = jest const mockUseRefreshTokenMutation = jest
.spyOn(mockDataProvider, 'useRefreshTokenMutation') .spyOn(authMutations, 'useRefreshTokenMutation')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useRefreshTokenMutationReturnValue); .mockReturnValue(useRefreshTokenMutationReturnValue);
const mockUseGetBannerQuery = jest const mockUseGetBannerQuery = jest
.spyOn(mockDataProvider, 'useGetBannerQuery') .spyOn(miscDataProvider, 'useGetBannerQuery')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetBannerQueryReturnValue); .mockReturnValue(useGetBannerQueryReturnValue);
const mockUseOutletContext = jest.spyOn(reactRouter, 'useOutletContext').mockReturnValue({ const mockUseOutletContext = jest.spyOn(reactRouter, 'useOutletContext').mockReturnValue({

View file

@ -1,8 +1,10 @@
import { render, getByTestId } from 'test/layout-test-utils'; import { render, getByTestId } from 'test/layout-test-utils';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import * as mockDataProvider from 'librechat-data-provider/react-query';
import type { TStartupConfig } from 'librechat-data-provider'; 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'; import Login from '../LoginForm';
jest.mock('librechat-data-provider/react-query'); jest.mock('librechat-data-provider/react-query');
@ -67,23 +69,23 @@ const setup = ({
}, },
} = {}) => { } = {}) => {
const mockUseLoginUser = jest const mockUseLoginUser = jest
.spyOn(authDataProvider, 'useLoginUserMutation') .spyOn(authMutations, 'useLoginUserMutation')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useLoginUserReturnValue); .mockReturnValue(useLoginUserReturnValue);
const mockUseGetUserQuery = jest const mockUseGetUserQuery = jest
.spyOn(mockDataProvider, 'useGetUserQuery') .spyOn(authQueries, 'useGetUserQuery')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetUserQueryReturnValue); .mockReturnValue(useGetUserQueryReturnValue);
const mockUseGetStartupConfig = jest const mockUseGetStartupConfig = jest
.spyOn(mockDataProvider, 'useGetStartupConfig') .spyOn(endpointQueries, 'useGetStartupConfig')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetStartupConfigReturnValue); .mockReturnValue(useGetStartupConfigReturnValue);
const mockUseRefreshTokenMutation = jest const mockUseRefreshTokenMutation = jest
.spyOn(mockDataProvider, 'useRefreshTokenMutation') .spyOn(authMutations, 'useRefreshTokenMutation')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useRefreshTokenMutationReturnValue); .mockReturnValue(useRefreshTokenMutationReturnValue);
const mockUseGetBannerQuery = jest const mockUseGetBannerQuery = jest
.spyOn(mockDataProvider, 'useGetBannerQuery') .spyOn(miscDataProvider, 'useGetBannerQuery')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetBannerQueryReturnValue); .mockReturnValue(useGetBannerQueryReturnValue);
return { return {

View file

@ -3,6 +3,10 @@ import userEvent from '@testing-library/user-event';
import { render, waitFor, screen } from 'test/layout-test-utils'; import { render, waitFor, screen } from 'test/layout-test-utils';
import * as mockDataProvider from 'librechat-data-provider/react-query'; import * as mockDataProvider from 'librechat-data-provider/react-query';
import type { TStartupConfig } from 'librechat-data-provider'; 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 Registration from '~/components/Auth/Registration';
import AuthLayout from '~/components/Auth/AuthLayout'; import AuthLayout from '~/components/Auth/AuthLayout';
@ -62,22 +66,22 @@ const setup = ({
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useRegisterUserMutationReturnValue); .mockReturnValue(useRegisterUserMutationReturnValue);
const mockUseGetUserQuery = jest const mockUseGetUserQuery = jest
.spyOn(mockDataProvider, 'useGetUserQuery') .spyOn(authQueries, 'useGetUserQuery')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetUserQueryReturnValue); .mockReturnValue(useGetUserQueryReturnValue);
const mockUseGetStartupConfig = jest const mockUseGetStartupConfig = jest
.spyOn(mockDataProvider, 'useGetStartupConfig') .spyOn(endpointQueries, 'useGetStartupConfig')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetStartupConfigReturnValue); .mockReturnValue(useGetStartupConfigReturnValue);
const mockUseRefreshTokenMutation = jest const mockUseRefreshTokenMutation = jest
.spyOn(mockDataProvider, 'useRefreshTokenMutation') .spyOn(authMutations, 'useRefreshTokenMutation')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useRefreshTokenMutationReturnValue); .mockReturnValue(useRefreshTokenMutationReturnValue);
const mockUseOutletContext = jest.spyOn(reactRouter, 'useOutletContext').mockReturnValue({ const mockUseOutletContext = jest.spyOn(reactRouter, 'useOutletContext').mockReturnValue({
startupConfig: useGetStartupConfigReturnValue.data, startupConfig: useGetStartupConfigReturnValue.data,
}); });
const mockUseGetBannerQuery = jest const mockUseGetBannerQuery = jest
.spyOn(mockDataProvider, 'useGetBannerQuery') .spyOn(miscDataProvider, 'useGetBannerQuery')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetBannerQueryReturnValue); .mockReturnValue(useGetBannerQueryReturnValue);
const renderResult = render( const renderResult = render(

View file

@ -1,8 +1,8 @@
import { useEffect, useRef } from 'react';
import { XIcon } from 'lucide-react'; import { XIcon } from 'lucide-react';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { useGetBannerQuery } from 'librechat-data-provider/react-query'; import { useGetBannerQuery } from '~/data-provider';
import store from '~/store'; import store from '~/store';
import { useEffect, useRef } from 'react';
export const Banner = ({ onHeightChange }: { onHeightChange?: (height: number) => void }) => { export const Banner = ({ onHeightChange }: { onHeightChange?: (height: number) => void }) => {
const { data: banner } = useGetBannerQuery(); const { data: banner } = useGetBannerQuery();

View file

@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import TagManager from 'react-gtm-module'; import TagManager from 'react-gtm-module';
import { Constants } from 'librechat-data-provider'; import { Constants } from 'librechat-data-provider';
import { useGetStartupConfig } from 'librechat-data-provider/react-query'; import { useGetStartupConfig } from '~/data-provider';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
export default function Footer({ className }: { className?: string }) { export default function Footer({ className }: { className?: string }) {

View file

@ -1,9 +1,9 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useOutletContext } from 'react-router-dom'; import { useOutletContext } from 'react-router-dom';
import { getConfigDefaults, PermissionTypes, Permissions } from 'librechat-data-provider'; import { getConfigDefaults, PermissionTypes, Permissions } from 'librechat-data-provider';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import type { ContextType } from '~/common'; import type { ContextType } from '~/common';
import { EndpointsMenu, ModelSpecsMenu, PresetsMenu, HeaderNewChat } from './Menus'; import { EndpointsMenu, ModelSpecsMenu, PresetsMenu, HeaderNewChat } from './Menus';
import { useGetStartupConfig } from '~/data-provider';
import ExportAndShareMenu from './ExportAndShareMenu'; import ExportAndShareMenu from './ExportAndShareMenu';
import { useMediaQuery, useHasAccess } from '~/hooks'; import { useMediaQuery, useHasAccess } from '~/hooks';
import HeaderOptions from './Input/HeaderOptions'; import HeaderOptions from './Input/HeaderOptions';

View file

@ -1,8 +1,8 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { TConversation, TEndpointOption, TPreset } from 'librechat-data-provider'; import type { TConversation, TEndpointOption, TPreset } from 'librechat-data-provider';
import type { SetterOrUpdater } from 'recoil'; import type { SetterOrUpdater } from 'recoil';
import useGetSender from '~/hooks/Conversations/useGetSender'; import useGetSender from '~/hooks/Conversations/useGetSender';
import { useGetEndpointsQuery } from '~/data-provider';
import { EndpointIcon } from '~/components/Endpoints'; import { EndpointIcon } from '~/components/Endpoints';
import { getPresetTitle } from '~/utils'; import { getPresetTitle } from '~/utils';

View file

@ -2,8 +2,8 @@ import * as Ariakit from '@ariakit/react';
import React, { useRef, useState, useMemo } from 'react'; import React, { useRef, useState, useMemo } from 'react';
import { FileSearch, ImageUpIcon, TerminalSquareIcon } from 'lucide-react'; import { FileSearch, ImageUpIcon, TerminalSquareIcon } from 'lucide-react';
import { EToolResources, EModelEndpoint } from 'librechat-data-provider'; import { EToolResources, EModelEndpoint } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import { FileUpload, TooltipAnchor, DropdownPopup } from '~/components/ui'; import { FileUpload, TooltipAnchor, DropdownPopup } from '~/components/ui';
import { useGetEndpointsQuery } from '~/data-provider';
import { AttachmentIcon } from '~/components/svg'; import { AttachmentIcon } from '~/components/svg';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
import { cn } from '~/utils'; import { cn } from '~/utils';

View file

@ -1,8 +1,8 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { EModelEndpoint, EToolResources } from 'librechat-data-provider'; import { EModelEndpoint, EToolResources } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import { FileSearch, ImageUpIcon, TerminalSquareIcon } from 'lucide-react'; import { FileSearch, ImageUpIcon, TerminalSquareIcon } from 'lucide-react';
import OGDialogTemplate from '~/components/ui/OGDialogTemplate'; import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
import { useGetEndpointsQuery } from '~/data-provider';
import useLocalize from '~/hooks/useLocalize'; import useLocalize from '~/hooks/useLocalize';
import { OGDialog } from '~/components/ui'; import { OGDialog } from '~/components/ui';

View file

@ -2,13 +2,13 @@ import { useRecoilState } from 'recoil';
import { Settings2 } from 'lucide-react'; import { Settings2 } from 'lucide-react';
import { Root, Anchor } from '@radix-ui/react-popover'; import { Root, Anchor } from '@radix-ui/react-popover';
import { useState, useEffect, useMemo } from 'react'; import { useState, useEffect, useMemo } from 'react';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import { tConvoUpdateSchema, EModelEndpoint, isParamEndpoint } from 'librechat-data-provider'; import { tConvoUpdateSchema, EModelEndpoint, isParamEndpoint } from 'librechat-data-provider';
import type { TPreset, TInterfaceConfig } from 'librechat-data-provider'; import type { TPreset, TInterfaceConfig } from 'librechat-data-provider';
import { EndpointSettings, SaveAsPresetDialog, AlternativeSettings } from '~/components/Endpoints'; import { EndpointSettings, SaveAsPresetDialog, AlternativeSettings } from '~/components/Endpoints';
import { PluginStoreDialog, TooltipAnchor } from '~/components'; import { PluginStoreDialog, TooltipAnchor } from '~/components';
import { ModelSelect } from '~/components/Input/ModelSelect'; import { ModelSelect } from '~/components/Input/ModelSelect';
import { useSetIndexOptions, useLocalize } from '~/hooks'; import { useSetIndexOptions, useLocalize } from '~/hooks';
import { useGetEndpointsQuery } from '~/data-provider';
import OptionsPopover from './OptionsPopover'; import OptionsPopover from './OptionsPopover';
import PopoverButtons from './PopoverButtons'; import PopoverButtons from './PopoverButtons';
import { useChatContext } from '~/Providers'; import { useChatContext } from '~/Providers';

View file

@ -1,10 +1,13 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { EModelEndpoint, Constants } from 'librechat-data-provider'; 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 * as t from 'librechat-data-provider';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useChatContext, useAgentsMapContext, useAssistantsMapContext } from '~/Providers'; 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 ConvoIcon from '~/components/Endpoints/ConvoIcon';
import { getIconEndpoint, getEntity, cn } from '~/utils'; import { getIconEndpoint, getEntity, cn } from '~/utils';
import { useLocalize, useSubmitMessage } from '~/hooks'; import { useLocalize, useSubmitMessage } from '~/hooks';

View file

@ -2,12 +2,12 @@ import { useState } from 'react';
import { Settings } from 'lucide-react'; import { Settings } from 'lucide-react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { EModelEndpoint } from 'librechat-data-provider'; import { EModelEndpoint } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { TConversation } from 'librechat-data-provider'; import type { TConversation } from 'librechat-data-provider';
import type { FC } from 'react'; import type { FC } from 'react';
import { cn, getConvoSwitchLogic, getEndpointField, getIconKey } from '~/utils'; import { cn, getConvoSwitchLogic, getEndpointField, getIconKey } from '~/utils';
import { useLocalize, useUserKey, useDefaultConvo } from '~/hooks'; import { useLocalize, useUserKey, useDefaultConvo } from '~/hooks';
import { SetKeyDialog } from '~/components/Input/SetKeyDialog'; import { SetKeyDialog } from '~/components/Input/SetKeyDialog';
import { useGetEndpointsQuery } from '~/data-provider';
import { useChatContext } from '~/Providers'; import { useChatContext } from '~/Providers';
import { icons } from './Icons'; import { icons } from './Icons';
import store from '~/store'; import store from '~/store';

View file

@ -6,7 +6,7 @@ import {
PermissionTypes, PermissionTypes,
Permissions, Permissions,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query'; import { useGetEndpointsQuery } from '~/data-provider';
import MenuSeparator from '../UI/MenuSeparator'; import MenuSeparator from '../UI/MenuSeparator';
import { getEndpointField } from '~/utils'; import { getEndpointField } from '~/utils';
import { useHasAccess } from '~/hooks'; import { useHasAccess } from '~/hooks';

View file

@ -1,9 +1,9 @@
import { useCallback, useRef } from 'react'; import { useCallback, useRef } from 'react';
import { alternateName } from 'librechat-data-provider'; import { alternateName } from 'librechat-data-provider';
import { Content, Portal, Root } from '@radix-ui/react-popover'; import { Content, Portal, Root } from '@radix-ui/react-popover';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { FC, KeyboardEvent } from 'react'; import type { FC, KeyboardEvent } from 'react';
import { useChatContext, useAgentsMapContext, useAssistantsMapContext } from '~/Providers'; import { useChatContext, useAgentsMapContext, useAssistantsMapContext } from '~/Providers';
import { useGetEndpointsQuery } from '~/data-provider';
import { mapEndpoints, getEntity } from '~/utils'; import { mapEndpoints, getEntity } from '~/utils';
import EndpointItems from './Endpoints/MenuItems'; import EndpointItems from './Endpoints/MenuItems';
import useLocalize from '~/hooks/useLocalize'; import useLocalize from '~/hooks/useLocalize';

View file

@ -1,13 +1,13 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { useMemo, useCallback, useRef } from 'react'; import { useMemo, useCallback, useRef } from 'react';
import { Content, Portal, Root } from '@radix-ui/react-popover'; 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 { EModelEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
import type { TModelSpec, TConversation, TEndpointsConfig } from 'librechat-data-provider'; import type { TModelSpec, TConversation, TEndpointsConfig } from 'librechat-data-provider';
import type { KeyboardEvent } from 'react'; import type { KeyboardEvent } from 'react';
import { useChatContext, useAssistantsMapContext } from '~/Providers'; import { useChatContext, useAssistantsMapContext } from '~/Providers';
import { useDefaultConvo, useNewConvo, useLocalize } from '~/hooks'; import { useDefaultConvo, useNewConvo, useLocalize } from '~/hooks';
import { getConvoSwitchLogic, getModelSpecIconURL } from '~/utils'; import { getConvoSwitchLogic, getModelSpecIconURL } from '~/utils';
import { useGetEndpointsQuery } from '~/data-provider';
import MenuButton from './MenuButton'; import MenuButton from './MenuButton';
import ModelSpecs from './ModelSpecs'; import ModelSpecs from './ModelSpecs';
import store from '~/store'; import store from '~/store';

View file

@ -2,7 +2,6 @@ import { useRecoilState } from 'recoil';
import { useCallback, useEffect, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { QueryKeys, isAgentsEndpoint } from 'librechat-data-provider'; import { QueryKeys, isAgentsEndpoint } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { TModelsConfig, TEndpointsConfig } from 'librechat-data-provider'; import type { TModelsConfig, TEndpointsConfig } from 'librechat-data-provider';
import { import {
cn, cn,
@ -16,6 +15,7 @@ import { useSetIndexOptions, useLocalize, useDebouncedInput } from '~/hooks';
import PopoverButtons from '~/components/Chat/Input/PopoverButtons'; import PopoverButtons from '~/components/Chat/Input/PopoverButtons';
import DialogTemplate from '~/components/ui/DialogTemplate'; import DialogTemplate from '~/components/ui/DialogTemplate';
import { EndpointSettings } from '~/components/Endpoints'; import { EndpointSettings } from '~/components/Endpoints';
import { useGetEndpointsQuery } from '~/data-provider';
import { useChatContext } from '~/Providers'; import { useChatContext } from '~/Providers';
import store from '~/store'; import store from '~/store';

View file

@ -1,7 +1,6 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { Close } from '@radix-ui/react-popover'; import { Close } from '@radix-ui/react-popover';
import { Flipper, Flipped } from 'react-flip-toolkit'; import { Flipper, Flipped } from 'react-flip-toolkit';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { FC } from 'react'; import type { FC } from 'react';
import type { TPreset } from 'librechat-data-provider'; import type { TPreset } from 'librechat-data-provider';
import { getPresetTitle, getEndpointField, getIconKey } from '~/utils'; 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 { PinIcon, EditIcon, TrashIcon } from '~/components/svg';
import { Dialog, DialogTrigger, Label } from '~/components/ui'; import { Dialog, DialogTrigger, Label } from '~/components/ui';
import DialogTemplate from '~/components/ui/DialogTemplate'; import DialogTemplate from '~/components/ui/DialogTemplate';
import { useGetEndpointsQuery } from '~/data-provider';
import { MenuSeparator, MenuItem } from '../UI'; import { MenuSeparator, MenuItem } from '../UI';
import { icons } from '../Endpoints/Icons'; import { icons } from '../Endpoints/Icons';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';

View file

@ -1,9 +1,9 @@
import React, { useMemo, memo } from 'react'; import React, { useMemo, memo } from 'react';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { Assistant, Agent } from 'librechat-data-provider'; import type { Assistant, Agent } from 'librechat-data-provider';
import type { TMessageIcon } from '~/common'; import type { TMessageIcon } from '~/common';
import { getEndpointField, getIconEndpoint, logger } from '~/utils'; import { getEndpointField, getIconEndpoint, logger } from '~/utils';
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL'; import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
import { useGetEndpointsQuery } from '~/data-provider';
import Icon from '~/components/Endpoints/Icon'; import Icon from '~/components/Endpoints/Icon';
const MessageIcon = memo( const MessageIcon = memo(

View file

@ -1,10 +1,9 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import { FileSources, LocalStorageKeys, getConfigDefaults } from 'librechat-data-provider'; import { FileSources, LocalStorageKeys, getConfigDefaults } from 'librechat-data-provider';
import type { ExtendedFile } from '~/common'; import type { ExtendedFile } from '~/common';
import { useDeleteFilesMutation, useGetStartupConfig } from '~/data-provider';
import DragDropWrapper from '~/components/Chat/Input/Files/DragDropWrapper'; import DragDropWrapper from '~/components/Chat/Input/Files/DragDropWrapper';
import { useDeleteFilesMutation } from '~/data-provider';
import Artifacts from '~/components/Artifacts/Artifacts'; import Artifacts from '~/components/Artifacts/Artifacts';
import { SidePanel } from '~/components/SidePanel'; import { SidePanel } from '~/components/SidePanel';
import { useSetFilesToDelete } from '~/hooks'; import { useSetFilesToDelete } from '~/hooks';

View file

@ -3,12 +3,12 @@ import { useRecoilValue } from 'recoil';
import { Check, X } from 'lucide-react'; import { Check, X } from 'lucide-react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { Constants } from 'librechat-data-provider'; import { Constants } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react'; import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
import type { TConversation } from 'librechat-data-provider'; import type { TConversation } from 'librechat-data-provider';
import { useNavigateToConvo, useMediaQuery, useLocalize } from '~/hooks'; import { useNavigateToConvo, useMediaQuery, useLocalize } from '~/hooks';
import { useUpdateConversationMutation } from '~/data-provider'; import { useUpdateConversationMutation } from '~/data-provider';
import EndpointIcon from '~/components/Endpoints/EndpointIcon'; import EndpointIcon from '~/components/Endpoints/EndpointIcon';
import { useGetEndpointsQuery } from '~/data-provider';
import { NotificationSeverity } from '~/common'; import { NotificationSeverity } from '~/common';
import { useToastContext } from '~/Providers'; import { useToastContext } from '~/Providers';
import { ConvoOptions } from './ConvoOptions'; import { ConvoOptions } from './ConvoOptions';

View file

@ -1,12 +1,11 @@
import { useState, useId, useRef, memo } from 'react'; import { useState, useId, useRef, memo } from 'react';
import * as Menu from '@ariakit/react/menu'; import * as Menu from '@ariakit/react/menu';
import { Ellipsis, Share2, Copy, Archive, Pen, Trash } from 'lucide-react'; 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 { MouseEvent } from 'react';
import type * as t from '~/common'; import type * as t from '~/common';
import { useDuplicateConversationMutation, useGetStartupConfig } from '~/data-provider';
import { useLocalize, useArchiveHandler, useNavigateToConvo } from '~/hooks'; import { useLocalize, useArchiveHandler, useNavigateToConvo } from '~/hooks';
import { useToastContext, useChatContext } from '~/Providers'; import { useToastContext, useChatContext } from '~/Providers';
import { useDuplicateConversationMutation } from '~/data-provider';
import { DropdownPopup } from '~/components/ui'; import { DropdownPopup } from '~/components/ui';
import DeleteButton from './DeleteButton'; import DeleteButton from './DeleteButton';
import ShareButton from './ShareButton'; import ShareButton from './ShareButton';

View file

@ -1,9 +1,10 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { SettingsViews, TConversation } from 'librechat-data-provider'; 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 type { TSettingsProps } from '~/common';
import { getSettings } from './Settings'; import { useGetEndpointsQuery } from '~/data-provider';
import { cn, getEndpointField } from '~/utils'; import { cn, getEndpointField } from '~/utils';
import { getSettings } from './Settings';
import store from '~/store'; import store from '~/store';
export default function Settings({ export default function Settings({

View file

@ -1,11 +1,11 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useForm, FormProvider } from 'react-hook-form'; import { useForm, FormProvider } from 'react-hook-form';
import { EModelEndpoint, alternateName, isAssistantsEndpoint } from 'librechat-data-provider'; import { EModelEndpoint, alternateName, isAssistantsEndpoint } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { TDialogProps } from '~/common'; import type { TDialogProps } from '~/common';
import OGDialogTemplate from '~/components/ui/OGDialogTemplate'; import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
import { RevokeKeysButton } from '~/components/Nav'; import { useGetEndpointsQuery } from '~/data-provider';
import { OGDialog, Dropdown } from '~/components/ui'; import { OGDialog, Dropdown } from '~/components/ui';
import { RevokeKeysButton } from '~/components/Nav';
import { useUserKey, useLocalize } from '~/hooks'; import { useUserKey, useLocalize } from '~/hooks';
import { useToastContext } from '~/Providers'; import { useToastContext } from '~/Providers';
import CustomConfig from './CustomEndpoint'; import CustomConfig from './CustomEndpoint';
@ -118,7 +118,7 @@ const SetKeyDialog = ({
if (isOpenAIBase && key === 'baseURL') { if (isOpenAIBase && key === 'baseURL') {
return false; return false;
} }
if (key === 'baseURL' && !userProvideURL) { if (key === 'baseURL' && !(userProvideURL ?? false)) {
return false; return false;
} }
return data[key] === ''; return data[key] === '';
@ -205,7 +205,7 @@ const SetKeyDialog = ({
leftButtons={ leftButtons={
<RevokeKeysButton <RevokeKeysButton
endpoint={endpoint} endpoint={endpoint}
disabled={!expiryTime} disabled={!(expiryTime ?? '')}
setDialogOpen={onOpenChange} setDialogOpen={onOpenChange}
/> />
} }

View file

@ -1,8 +1,8 @@
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react'; import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react';
import { useCallback, memo, ReactNode } from '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 { ChevronDownIcon, LucideProps } from 'lucide-react';
import type { TResPlugin, TInput } from 'librechat-data-provider';
import { useGetEndpointsQuery } from '~/data-provider';
import { useShareContext } from '~/Providers'; import { useShareContext } from '~/Providers';
import { cn, formatJSON } from '~/utils'; import { cn, formatJSON } from '~/utils';
import { Spinner } from '~/components'; import { Spinner } from '~/components';
@ -47,7 +47,7 @@ const Plugin: React.FC<PluginProps> = ({ plugin }) => {
if (pluginKey === 'n/a' || pluginKey === 'self reflection') { if (pluginKey === 'n/a' || pluginKey === 'self reflection') {
return pluginKey; return pluginKey;
} }
return plugins?.[pluginKey] ?? 'self reflection'; return plugins[pluginKey] ?? 'self reflection';
}, },
[plugins], [plugins],
); );
@ -105,7 +105,7 @@ const Plugin: React.FC<PluginProps> = ({ plugin }) => {
<DisclosurePanel className="mt-3 flex max-w-full flex-col gap-3"> <DisclosurePanel className="mt-3 flex max-w-full flex-col gap-3">
<CodeBlock <CodeBlock
lang={latestPlugin ? `REQUEST TO ${latestPlugin?.toUpperCase()}` : 'REQUEST'} lang={latestPlugin ? `REQUEST TO ${latestPlugin.toUpperCase()}` : 'REQUEST'}
codeChildren={formatInputs(plugin.inputs ?? [])} codeChildren={formatInputs(plugin.inputs ?? [])}
plugin={true} plugin={true}
classProp="max-h-[450px]" classProp="max-h-[450px]"
@ -113,7 +113,7 @@ const Plugin: React.FC<PluginProps> = ({ plugin }) => {
{plugin.outputs && plugin.outputs.length > 0 && ( {plugin.outputs && plugin.outputs.length > 0 && (
<CodeBlock <CodeBlock
lang={ lang={
latestPlugin ? `RESPONSE FROM ${latestPlugin?.toUpperCase()}` : 'RESPONSE' latestPlugin ? `RESPONSE FROM ${latestPlugin.toUpperCase()}` : 'RESPONSE'
} }
codeChildren={formatJSON(plugin.outputs ?? '')} codeChildren={formatJSON(plugin.outputs ?? '')}
plugin={true} plugin={true}

View file

@ -2,8 +2,8 @@ import { useRecoilState } from 'recoil';
import * as Select from '@ariakit/react/select'; import * as Select from '@ariakit/react/select';
import { Fragment, useState, memo } from 'react'; import { Fragment, useState, memo } from 'react';
import { FileText, LogOut } from 'lucide-react'; import { FileText, LogOut } from 'lucide-react';
import { useGetUserBalance, useGetStartupConfig } from 'librechat-data-provider/react-query';
import { LinkIcon, GearIcon, DropdownMenuSeparator } from '~/components'; import { LinkIcon, GearIcon, DropdownMenuSeparator } from '~/components';
import { useGetStartupConfig, useGetUserBalance } from '~/data-provider';
import FilesView from '~/components/Chat/Input/Files/FilesView'; import FilesView from '~/components/Chat/Input/Files/FilesView';
import { useAuthContext } from '~/hooks/AuthContext'; import { useAuthContext } from '~/hooks/AuthContext';
import useAvatar from '~/hooks/Messages/useAvatar'; import useAvatar from '~/hooks/Messages/useAvatar';

View file

@ -3,11 +3,11 @@ import { useRecoilValue } from 'recoil';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { QueryKeys, Constants } from 'librechat-data-provider'; import { QueryKeys, Constants } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { TConversation, TMessage } from 'librechat-data-provider'; import type { TConversation, TMessage } from 'librechat-data-provider';
import { getEndpointField, getIconEndpoint, getIconKey } from '~/utils'; import { getEndpointField, getIconEndpoint, getIconKey } from '~/utils';
import { icons } from '~/components/Chat/Menus/Endpoints/Icons'; import { icons } from '~/components/Chat/Menus/Endpoints/Icons';
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL'; import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
import { useGetEndpointsQuery } from '~/data-provider';
import { useLocalize, useNewConvo } from '~/hooks'; import { useLocalize, useNewConvo } from '~/hooks';
import { NewChatIcon } from '~/components/svg'; import { NewChatIcon } from '~/components/svg';
import { cn } from '~/utils'; import { cn } from '~/utils';

View file

@ -2,6 +2,8 @@ import { render, screen, fireEvent } from 'test/layout-test-utils';
import PluginStoreDialog from '../PluginStoreDialog'; import PluginStoreDialog from '../PluginStoreDialog';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import * as mockDataProvider from 'librechat-data-provider/react-query'; import * as mockDataProvider from 'librechat-data-provider/react-query';
import * as authMutations from '~/data-provider/Auth/mutations';
import * as authQueries from '~/data-provider/Auth/queries';
jest.mock('librechat-data-provider/react-query'); jest.mock('librechat-data-provider/react-query');
@ -143,11 +145,11 @@ const setup = ({
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useUpdateUserPluginsMutationReturnValue); .mockReturnValue(useUpdateUserPluginsMutationReturnValue);
const mockUseGetUserQuery = jest const mockUseGetUserQuery = jest
.spyOn(mockDataProvider, 'useGetUserQuery') .spyOn(authQueries, 'useGetUserQuery')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetUserQueryReturnValue); .mockReturnValue(useGetUserQueryReturnValue);
const mockUseRefreshTokenMutation = jest const mockUseRefreshTokenMutation = jest
.spyOn(mockDataProvider, 'useRefreshTokenMutation') .spyOn(authMutations, 'useRefreshTokenMutation')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult //@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useRefreshTokenMutationReturnValue); .mockReturnValue(useRefreshTokenMutationReturnValue);
const mockSetIsOpen = jest.fn(); const mockSetIsOpen = jest.fn();

View file

@ -1,9 +1,9 @@
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { PermissionTypes, Permissions } from 'librechat-data-provider'; import { PermissionTypes, Permissions } from 'librechat-data-provider';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import type { TPromptGroup, TStartupConfig } from 'librechat-data-provider'; import type { TPromptGroup, TStartupConfig } from 'librechat-data-provider';
import DashGroupItem from '~/components/Prompts/Groups/DashGroupItem'; import DashGroupItem from '~/components/Prompts/Groups/DashGroupItem';
import ChatGroupItem from '~/components/Prompts/Groups/ChatGroupItem'; import ChatGroupItem from '~/components/Prompts/Groups/ChatGroupItem';
import { useGetStartupConfig } from '~/data-provider';
import { useLocalize, useHasAccess } from '~/hooks'; import { useLocalize, useHasAccess } from '~/hooks';
import { Button, Skeleton } from '~/components/ui'; import { Button, Skeleton } from '~/components/ui';

View file

@ -2,7 +2,6 @@ import React, { useEffect, useMemo } from 'react';
import { Share2Icon } from 'lucide-react'; import { Share2Icon } from 'lucide-react';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { Permissions } from 'librechat-data-provider'; import { Permissions } from 'librechat-data-provider';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import type { import type {
TPromptGroup, TPromptGroup,
TStartupConfig, TStartupConfig,
@ -15,7 +14,7 @@ import {
OGDialogTrigger, OGDialogTrigger,
OGDialogClose, OGDialogClose,
} from '~/components/ui'; } from '~/components/ui';
import { useUpdatePromptGroup } from '~/data-provider'; import { useUpdatePromptGroup, useGetStartupConfig } from '~/data-provider';
import { Button, Switch } from '~/components/ui'; import { Button, Switch } from '~/components/ui';
import { useToastContext } from '~/Providers'; import { useToastContext } from '~/Providers';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';

View file

@ -1,7 +1,8 @@
import { memo } from 'react'; import { memo } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useGetSharedMessages, useGetStartupConfig } from 'librechat-data-provider/react-query'; import { useGetSharedMessages } from 'librechat-data-provider/react-query';
import { useLocalize, useDocumentTitle } from '~/hooks'; import { useLocalize, useDocumentTitle } from '~/hooks';
import { useGetStartupConfig } from '~/data-provider';
import { ShareContext } from '~/Providers'; import { ShareContext } from '~/Providers';
import { Spinner } from '~/components/svg'; import { Spinner } from '~/components/svg';
import MessagesView from './MessagesView'; import MessagesView from './MessagesView';

View file

@ -1,9 +1,8 @@
import { useState, useEffect, useMemo } from 'react'; import { useState, useEffect, useMemo } from 'react';
import { EModelEndpoint } from 'librechat-data-provider'; import { EModelEndpoint } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { ActionsEndpoint } from '~/common'; import type { ActionsEndpoint } from '~/common';
import type { Action, TConfig, TEndpointsConfig } from 'librechat-data-provider'; import type { Action, TConfig, TEndpointsConfig } from 'librechat-data-provider';
import { useGetActionsQuery } from '~/data-provider'; import { useGetActionsQuery, useGetEndpointsQuery } from '~/data-provider';
import { useChatContext } from '~/Providers'; import { useChatContext } from '~/Providers';
import ActionsPanel from './ActionsPanel'; import ActionsPanel from './ActionsPanel';
import AgentPanel from './AgentPanel'; import AgentPanel from './AgentPanel';

View file

@ -1,14 +1,13 @@
import { Plus, EarthIcon } from 'lucide-react'; import { Plus, EarthIcon } from 'lucide-react';
import { useCallback, useEffect, useRef } from 'react'; import { useCallback, useEffect, useRef } from 'react';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import { AgentCapabilities, defaultAgentFormValues } from 'librechat-data-provider'; import { AgentCapabilities, defaultAgentFormValues } from 'librechat-data-provider';
import type { UseMutationResult, QueryObserverResult } from '@tanstack/react-query'; import type { UseMutationResult, QueryObserverResult } from '@tanstack/react-query';
import type { Agent, AgentCreateParams } from 'librechat-data-provider'; import type { Agent, AgentCreateParams } from 'librechat-data-provider';
import type { UseFormReset } from 'react-hook-form'; import type { UseFormReset } from 'react-hook-form';
import type { TAgentCapabilities, AgentForm, TAgentOption } from '~/common'; import type { TAgentCapabilities, AgentForm, TAgentOption } from '~/common';
import { cn, createDropdownSetter, createProviderOption, processAgentOption } from '~/utils'; import { cn, createDropdownSetter, createProviderOption, processAgentOption } from '~/utils';
import { useListAgentsQuery, useGetStartupConfig } from '~/data-provider';
import SelectDropDown from '~/components/ui/SelectDropDown'; import SelectDropDown from '~/components/ui/SelectDropDown';
import { useListAgentsQuery } from '~/data-provider';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
const keys = new Set(Object.keys(defaultAgentFormValues)); const keys = new Set(Object.keys(defaultAgentFormValues));

View file

@ -2,12 +2,12 @@ import React, { useMemo, useEffect } from 'react';
import { ChevronLeft, RotateCcw } from 'lucide-react'; import { ChevronLeft, RotateCcw } from 'lucide-react';
import { getSettingsKeys } from 'librechat-data-provider'; import { getSettingsKeys } from 'librechat-data-provider';
import { useFormContext, useWatch, Controller } from 'react-hook-form'; import { useFormContext, useWatch, Controller } from 'react-hook-form';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type * as t from 'librechat-data-provider'; import type * as t from 'librechat-data-provider';
import type { AgentForm, AgentModelPanelProps, StringOption } from '~/common'; import type { AgentForm, AgentModelPanelProps, StringOption } from '~/common';
import { componentMapping } from '~/components/SidePanel/Parameters/components'; import { componentMapping } from '~/components/SidePanel/Parameters/components';
import { agentSettings } from '~/components/SidePanel/Parameters/settings'; import { agentSettings } from '~/components/SidePanel/Parameters/settings';
import { getEndpointField, cn, cardStyle } from '~/utils'; import { getEndpointField, cn, cardStyle } from '~/utils';
import { useGetEndpointsQuery } from '~/data-provider';
import { SelectDropDown } from '~/components/ui'; import { SelectDropDown } from '~/components/ui';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
import { Panel } from '~/common'; import { Panel } from '~/common';

View file

@ -2,7 +2,6 @@ import React, { useEffect, useMemo } from 'react';
import { Share2Icon } from 'lucide-react'; import { Share2Icon } from 'lucide-react';
import { useForm, Controller } from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { Permissions } from 'librechat-data-provider'; import { Permissions } from 'librechat-data-provider';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import type { TStartupConfig, AgentUpdateParams } from 'librechat-data-provider'; import type { TStartupConfig, AgentUpdateParams } from 'librechat-data-provider';
import { import {
Button, Button,
@ -13,7 +12,7 @@ import {
OGDialogContent, OGDialogContent,
OGDialogTrigger, OGDialogTrigger,
} from '~/components/ui'; } from '~/components/ui';
import { useUpdateAgentMutation } from '~/data-provider'; import { useUpdateAgentMutation, useGetStartupConfig } from '~/data-provider';
import { cn, removeFocusOutlines } from '~/utils'; import { cn, removeFocusOutlines } from '~/utils';
import { useToastContext } from '~/Providers'; import { useToastContext } from '~/Providers';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';

View file

@ -1,9 +1,12 @@
import { useState, useEffect, useMemo } from 'react'; import { useState, useEffect, useMemo } from 'react';
import { defaultAssistantsVersion } from 'librechat-data-provider'; import { defaultAssistantsVersion } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { Action, TEndpointsConfig, AssistantsEndpoint } from 'librechat-data-provider'; import type { Action, TEndpointsConfig, AssistantsEndpoint } from 'librechat-data-provider';
import type { ActionsEndpoint } from '~/common'; import type { ActionsEndpoint } from '~/common';
import { useGetActionsQuery, useGetAssistantDocsQuery } from '~/data-provider'; import {
useGetActionsQuery,
useGetEndpointsQuery,
useGetAssistantDocsQuery,
} from '~/data-provider';
import AssistantPanel from './AssistantPanel'; import AssistantPanel from './AssistantPanel';
import { useChatContext } from '~/Providers'; import { useChatContext } from '~/Providers';
import ActionsPanel from './ActionsPanel'; import ActionsPanel from './ActionsPanel';

View file

@ -1,9 +1,9 @@
import React, { useMemo, useState, useEffect, useCallback } from 'react'; import React, { useMemo, useState, useEffect, useCallback } from 'react';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import { getSettingsKeys, tConvoUpdateSchema } from 'librechat-data-provider'; import { getSettingsKeys, tConvoUpdateSchema } from 'librechat-data-provider';
import type { TPreset } from 'librechat-data-provider'; import type { TPreset } from 'librechat-data-provider';
import { SaveAsPresetDialog } from '~/components/Endpoints'; import { SaveAsPresetDialog } from '~/components/Endpoints';
import { useSetIndexOptions, useLocalize } from '~/hooks'; import { useSetIndexOptions, useLocalize } from '~/hooks';
import { useGetEndpointsQuery } from '~/data-provider';
import { getEndpointField, logger } from '~/utils'; import { getEndpointField, logger } from '~/utils';
import { componentMapping } from './components'; import { componentMapping } from './components';
import { useChatContext } from '~/Providers'; import { useChatContext } from '~/Providers';

View file

@ -1,14 +1,11 @@
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import { getConfigDefaults } from 'librechat-data-provider'; import { getConfigDefaults } from 'librechat-data-provider';
import { useUserKeyQuery } from 'librechat-data-provider/react-query';
import { useState, useRef, useCallback, useEffect, useMemo, memo } from 'react'; import { useState, useRef, useCallback, useEffect, useMemo, memo } from 'react';
import {
useGetEndpointsQuery,
useGetStartupConfig,
useUserKeyQuery,
} from 'librechat-data-provider/react-query';
import type { TEndpointsConfig, TInterfaceConfig } from 'librechat-data-provider'; import type { TEndpointsConfig, TInterfaceConfig } from 'librechat-data-provider';
import type { ImperativePanelHandle } from 'react-resizable-panels'; import type { ImperativePanelHandle } from 'react-resizable-panels';
import { ResizableHandleAlt, ResizablePanel, ResizablePanelGroup } from '~/components/ui/Resizable'; import { ResizableHandleAlt, ResizablePanel, ResizablePanelGroup } from '~/components/ui/Resizable';
import { useGetEndpointsQuery, useGetStartupConfig } from '~/data-provider';
import { useMediaQuery, useLocalStorage, useLocalize } from '~/hooks'; import { useMediaQuery, useLocalStorage, useLocalize } from '~/hooks';
import useSideNavLinks from '~/hooks/Nav/useSideNavLinks'; import useSideNavLinks from '~/hooks/Nav/useSideNavLinks';
import NavToggle from '~/components/Nav/NavToggle'; import NavToggle from '~/components/Nav/NavToggle';

View file

@ -1 +1,2 @@
export * from './queries';
export * from './mutations'; export * from './mutations';

View file

@ -1,6 +1,6 @@
import { useResetRecoilState } from 'recoil'; import { useResetRecoilState, useSetRecoilState } from 'recoil';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { MutationKeys, dataService } from 'librechat-data-provider'; import { MutationKeys, dataService, request } from 'librechat-data-provider';
import type { UseMutationResult } from '@tanstack/react-query'; import type { UseMutationResult } from '@tanstack/react-query';
import type * as t from 'librechat-data-provider'; import type * as t from 'librechat-data-provider';
import useClearStates from '~/hooks/Config/useClearStates'; import useClearStates from '~/hooks/Config/useClearStates';
@ -9,15 +9,17 @@ import store from '~/store';
/* login/logout */ /* login/logout */
export const useLogoutUserMutation = ( export const useLogoutUserMutation = (
options?: t.LogoutOptions, options?: t.LogoutOptions,
): UseMutationResult<unknown, unknown, undefined, unknown> => { ): UseMutationResult<t.TLogoutResponse, unknown, undefined, unknown> => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const clearStates = useClearStates(); const clearStates = useClearStates();
const resetDefaultPreset = useResetRecoilState(store.defaultPreset); const resetDefaultPreset = useResetRecoilState(store.defaultPreset);
const setQueriesEnabled = useSetRecoilState<boolean>(store.queriesEnabled);
return useMutation([MutationKeys.logoutUser], { return useMutation([MutationKeys.logoutUser], {
mutationFn: () => dataService.logout(), mutationFn: () => dataService.logout(),
...(options || {}), ...(options || {}),
onSuccess: (...args) => { onSuccess: (...args) => {
setQueriesEnabled(false);
resetDefaultPreset(); resetDefaultPreset();
clearStates(); clearStates();
queryClient.removeQueries(); queryClient.removeQueries();
@ -26,20 +28,39 @@ export const useLogoutUserMutation = (
}); });
}; };
export const useLoginUserMutation = (): UseMutationResult< export const useLoginUserMutation = (
t.TLoginResponse, options?: t.MutationOptions<t.TLoginResponse, t.TLoginUser, unknown, unknown>,
unknown, ): UseMutationResult<t.TLoginResponse, unknown, t.TLoginUser, unknown> => {
t.TLoginUser,
unknown
> => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const clearStates = useClearStates(); const clearStates = useClearStates();
const resetDefaultPreset = useResetRecoilState(store.defaultPreset); const resetDefaultPreset = useResetRecoilState(store.defaultPreset);
return useMutation((payload: t.TLoginUser) => dataService.login(payload), { const setQueriesEnabled = useSetRecoilState<boolean>(store.queriesEnabled);
onMutate: () => { return useMutation([MutationKeys.loginUser], {
mutationFn: (payload: t.TLoginUser) => dataService.login(payload),
...(options || {}),
onMutate: (vars) => {
resetDefaultPreset(); resetDefaultPreset();
clearStates(); clearStates();
queryClient.removeQueries(); queryClient.removeQueries();
options?.onMutate?.(vars);
},
onSuccess: (...args) => {
setQueriesEnabled(true);
options?.onSuccess?.(...args);
},
});
};
export const useRefreshTokenMutation = (
options?: t.MutationOptions<t.TRefreshTokenResponse | undefined, undefined, unknown, unknown>,
): UseMutationResult<t.TRefreshTokenResponse | undefined, unknown, undefined, unknown> => {
const queryClient = useQueryClient();
return useMutation([MutationKeys.refreshToken], {
mutationFn: () => request.refreshToken(),
...(options || {}),
onMutate: (vars) => {
queryClient.removeQueries();
options?.onMutate?.(vars);
}, },
}); });
}; };

View file

@ -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<t.TUser>,
): QueryObserverResult<t.TUser> => {
const queriesEnabled = useRecoilValue<boolean>(store.queriesEnabled);
return useQuery<t.TUser>([QueryKeys.user], () => dataService.getUser(), {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
retry: false,
...config,
enabled: (config?.enabled ?? true) === true && queriesEnabled,
});
};

View file

@ -0,0 +1 @@
export * from './queries';

View file

@ -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 = <TData = t.TEndpointsConfig>(
config?: UseQueryOptions<t.TEndpointsConfig, unknown, TData>,
): QueryObserverResult<TData> => {
const queriesEnabled = useRecoilValue<boolean>(store.queriesEnabled);
return useQuery<t.TEndpointsConfig, unknown, TData>(
[QueryKeys.endpoints],
() => dataService.getAIEndpoints(),
{
staleTime: Infinity,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
...config,
enabled: (config?.enabled ?? true) === true && queriesEnabled,
},
);
};
export const useGetStartupConfig = (
config?: UseQueryOptions<t.TStartupConfig>,
): QueryObserverResult<t.TStartupConfig> => {
const queriesEnabled = useRecoilValue<boolean>(store.queriesEnabled);
return useQuery<t.TStartupConfig>(
[QueryKeys.startupConfig],
() => dataService.getStartupConfig(),
{
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
...config,
enabled: (config?.enabled ?? true) === true && queriesEnabled,
},
);
};

View file

@ -1,17 +1,21 @@
import { useRecoilValue } from 'recoil';
import { QueryKeys, dataService } from 'librechat-data-provider'; import { QueryKeys, dataService } from 'librechat-data-provider';
import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useQuery, useQueryClient } from '@tanstack/react-query';
import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query';
import type t from 'librechat-data-provider'; import type t from 'librechat-data-provider';
import { addFileToCache } from '~/utils'; import { addFileToCache } from '~/utils';
import store from '~/store';
export const useGetFiles = <TData = t.TFile[] | boolean>( export const useGetFiles = <TData = t.TFile[] | boolean>(
config?: UseQueryOptions<t.TFile[], unknown, TData>, config?: UseQueryOptions<t.TFile[], unknown, TData>,
): QueryObserverResult<TData, unknown> => { ): QueryObserverResult<TData, unknown> => {
const queriesEnabled = useRecoilValue<boolean>(store.queriesEnabled);
return useQuery<t.TFile[], unknown, TData>([QueryKeys.files], () => dataService.getFiles(), { return useQuery<t.TFile[], unknown, TData>([QueryKeys.files], () => dataService.getFiles(), {
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
refetchOnReconnect: false, refetchOnReconnect: false,
refetchOnMount: false, refetchOnMount: false,
...config, ...config,
enabled: (config?.enabled ?? true) === true && queriesEnabled,
}); });
}; };

View file

@ -0,0 +1 @@
export * from './queries';

View file

@ -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<t.TBannerResponse>,
): QueryObserverResult<t.TBannerResponse> => {
const queriesEnabled = useRecoilValue<boolean>(store.queriesEnabled);
return useQuery<t.TBannerResponse>([QueryKeys.banner], () => dataService.getBanner(), {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
...config,
enabled: (config?.enabled ?? true) === true && queriesEnabled,
});
};
export const useGetUserBalance = (
config?: UseQueryOptions<string>,
): QueryObserverResult<string> => {
const queriesEnabled = useRecoilValue<boolean>(store.queriesEnabled);
return useQuery<string>([QueryKeys.balance], () => dataService.getUserBalance(), {
refetchOnWindowFocus: true,
refetchOnReconnect: true,
refetchOnMount: true,
...config,
enabled: (config?.enabled ?? true) === true && queriesEnabled,
});
};
export const useGetSearchEnabledQuery = (
config?: UseQueryOptions<boolean>,
): QueryObserverResult<boolean> => {
const queriesEnabled = useRecoilValue<boolean>(store.queriesEnabled);
return useQuery<boolean>([QueryKeys.searchEnabled], () => dataService.getSearchEnabled(), {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
...config,
enabled: (config?.enabled ?? true) === true && queriesEnabled,
});
};

View file

@ -1,7 +1,9 @@
export * from './Auth'; export * from './Auth';
export * from './Agents'; export * from './Agents';
export * from './Endpoints';
export * from './Files'; export * from './Files';
export * from './Messages'; export * from './Messages';
export * from './Misc';
export * from './Tools'; export * from './Tools';
export * from './connection'; export * from './connection';
export * from './mutations'; export * from './mutations';

View file

@ -5,6 +5,7 @@ import {
defaultOrderQuery, defaultOrderQuery,
defaultAssistantsVersion, defaultAssistantsVersion,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import { useRecoilValue } from 'recoil';
import { useQuery, useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; import { useQuery, useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
import type { import type {
UseInfiniteQueryOptions, UseInfiniteQueryOptions,
@ -28,6 +29,7 @@ import type {
SharedLinksResponse, SharedLinksResponse,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import { findPageForConversation } from '~/utils'; import { findPageForConversation } from '~/utils';
import store from '~/store';
export const useGetPresetsQuery = ( export const useGetPresetsQuery = (
config?: UseQueryOptions<TPreset[]>, config?: UseQueryOptions<TPreset[]>,
@ -115,6 +117,7 @@ export const useConversationsInfiniteQuery = (
params?: ConversationListParams, params?: ConversationListParams,
config?: UseInfiniteQueryOptions<ConversationListResponse, unknown>, config?: UseInfiniteQueryOptions<ConversationListResponse, unknown>,
) => { ) => {
const queriesEnabled = useRecoilValue<boolean>(store.queriesEnabled);
return useInfiniteQuery<ConversationListResponse, unknown>( return useInfiniteQuery<ConversationListResponse, unknown>(
params?.isArchived === true ? [QueryKeys.archivedConversations] : [QueryKeys.allConversations], params?.isArchived === true ? [QueryKeys.archivedConversations] : [QueryKeys.allConversations],
({ pageParam = '' }) => ({ pageParam = '' }) =>
@ -135,6 +138,7 @@ export const useConversationsInfiniteQuery = (
refetchOnReconnect: false, refetchOnReconnect: false,
refetchOnMount: false, refetchOnMount: false,
...config, ...config,
enabled: (config?.enabled ?? true) === true && queriesEnabled,
}, },
); );
}; };
@ -156,7 +160,7 @@ export const useSharedLinksQuery = (
sortBy, sortBy,
sortDirection, sortDirection,
}), }),
getNextPageParam: (lastPage) => lastPage?.nextCursor ?? undefined, getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
keepPreviousData: true, keepPreviousData: true,
staleTime: 5 * 60 * 1000, // 5 minutes staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 30 * 60 * 1000, // 30 minutes cacheTime: 30 * 60 * 1000, // 30 minutes

View file

@ -7,12 +7,17 @@ import {
useCallback, useCallback,
createContext, createContext,
} from 'react'; } from 'react';
import { useRecoilState } from 'recoil';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useRecoilState, useRecoilValue } from 'recoil';
import { setTokenHeader, SystemRoles } from 'librechat-data-provider'; import { setTokenHeader, SystemRoles } from 'librechat-data-provider';
import { useGetUserQuery, useRefreshTokenMutation } from 'librechat-data-provider/react-query'; import type * as t from 'librechat-data-provider';
import type { TLoginResponse, TLoginUser } from 'librechat-data-provider'; import {
import { useLoginUserMutation, useLogoutUserMutation, useGetRole } from '~/data-provider'; useGetRole,
useGetUserQuery,
useLoginUserMutation,
useLogoutUserMutation,
useRefreshTokenMutation,
} from '~/data-provider';
import { TAuthConfig, TUserContext, TAuthContext, TResError } from '~/common'; import { TAuthConfig, TUserContext, TAuthContext, TResError } from '~/common';
import useTimeout from './useTimeout'; import useTimeout from './useTimeout';
import store from '~/store'; import store from '~/store';
@ -42,14 +47,20 @@ const AuthContextProvider = ({
const setUserContext = useCallback( const setUserContext = useCallback(
(userContext: TUserContext) => { (userContext: TUserContext) => {
const { token, isAuthenticated, user, redirect } = userContext; const { token, isAuthenticated, user, redirect } = userContext;
if (user) { setUser(user);
setUser(user);
}
setToken(token); setToken(token);
//@ts-ignore - ok for token to be undefined initially //@ts-ignore - ok for token to be undefined initially
setTokenHeader(token); setTokenHeader(token);
setIsAuthenticated(isAuthenticated); 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 }); navigate(redirect, { replace: true });
} }
}, },
@ -57,14 +68,25 @@ const AuthContextProvider = ({
); );
const doSetError = useTimeout({ callback: (error) => setError(error as string | undefined) }); 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({ const logoutUser = useLogoutUserMutation({
onSuccess: () => { onSuccess: (data) => {
setUserContext({ setUserContext({
token: undefined, token: undefined,
isAuthenticated: false, isAuthenticated: false,
user: undefined, user: undefined,
redirect: '/login', redirect: data.redirect ?? '/login',
}); });
}, },
onError: (error) => { onError: (error) => {
@ -77,24 +99,13 @@ const AuthContextProvider = ({
}); });
}, },
}); });
const refreshToken = useRefreshTokenMutation();
const logout = useCallback(() => logoutUser.mutate(undefined), [logoutUser]); const logout = useCallback(() => logoutUser.mutate(undefined), [logoutUser]);
const userQuery = useGetUserQuery({ enabled: !!(token ?? '') }); const userQuery = useGetUserQuery({ enabled: !!(token ?? '') });
const refreshToken = useRefreshTokenMutation();
const login = (data: TLoginUser) => { const login = (data: t.TLoginUser) => {
loginUser.mutate(data, { 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 silentRefresh = useCallback(() => { const silentRefresh = useCallback(() => {
@ -103,7 +114,7 @@ const AuthContextProvider = ({
return; return;
} }
refreshToken.mutate(undefined, { refreshToken.mutate(undefined, {
onSuccess: (data: TLoginResponse | undefined) => { onSuccess: (data: t.TRefreshTokenResponse | undefined) => {
const { user, token = '' } = data ?? {}; const { user, token = '' } = data ?? {};
if (token) { if (token) {
setUserContext({ token, isAuthenticated: true, user }); setUserContext({ token, isAuthenticated: true, user });

View file

@ -5,7 +5,6 @@ import { LocalStorageKeys } from 'librechat-data-provider';
import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query'; import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query';
import type { TStartupConfig, TPlugin, TUser } from 'librechat-data-provider'; import type { TStartupConfig, TPlugin, TUser } from 'librechat-data-provider';
import { mapPlugins, selectPlugins, processPlugins } from '~/utils'; import { mapPlugins, selectPlugins, processPlugins } from '~/utils';
import useConfigOverride from './useConfigOverride';
import store from '~/store'; import store from '~/store';
const pluginStore: TPlugin = { const pluginStore: TPlugin = {
@ -25,7 +24,6 @@ export default function useAppStartup({
startupConfig?: TStartupConfig; startupConfig?: TStartupConfig;
user?: TUser; user?: TUser;
}) { }) {
useConfigOverride();
const setAvailableTools = useSetRecoilState(store.availableTools); const setAvailableTools = useSetRecoilState(store.availableTools);
const [defaultPreset, setDefaultPreset] = useRecoilState(store.defaultPreset); const [defaultPreset, setDefaultPreset] = useRecoilState(store.defaultPreset);
const { data: allPlugins } = useAvailablePluginsQuery({ const { data: allPlugins } = useAvailablePluginsQuery({

View file

@ -1,11 +1,12 @@
import { useGetEndpointsQuery, useGetModelsQuery } from 'librechat-data-provider/react-query'; import { useGetModelsQuery } from 'librechat-data-provider/react-query';
import type { import type {
TConversation,
TPreset,
TEndpointsConfig, TEndpointsConfig,
TModelsConfig, TModelsConfig,
TConversation,
TPreset,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import { getDefaultEndpoint, buildDefaultConvo } from '~/utils'; import { getDefaultEndpoint, buildDefaultConvo } from '~/utils';
import { useGetEndpointsQuery } from '~/data-provider';
type TDefaultConvo = { conversation: Partial<TConversation>; preset?: Partial<TPreset> | null }; type TDefaultConvo = { conversation: Partial<TConversation>; preset?: Partial<TPreset> | null };

View file

@ -1,17 +1,19 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { useCallback, useRef, useEffect } from 'react'; import { useCallback, useRef, useEffect } from 'react';
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
import { LocalStorageKeys, isAssistantsEndpoint } from 'librechat-data-provider'; import { LocalStorageKeys, isAssistantsEndpoint } from 'librechat-data-provider';
import { useGetModelsQuery, useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { import type {
TPreset, TPreset,
TModelsConfig, TModelsConfig,
TConversation, TConversation,
TEndpointsConfig, TEndpointsConfig,
EModelEndpoint,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import type { SetterOrUpdater } from 'recoil'; import type { SetterOrUpdater } from 'recoil';
import type { AssistantListItem } from '~/common'; import type { AssistantListItem } from '~/common';
import { getEndpointField, buildDefaultConvo, getDefaultEndpoint } from '~/utils'; import { getEndpointField, buildDefaultConvo, getDefaultEndpoint } from '~/utils';
import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap'; import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap';
import { useGetEndpointsQuery } from '~/data-provider';
import { mainTextareaId } from '~/common'; import { mainTextareaId } from '~/common';
import store from '~/store'; import store from '~/store';
@ -32,7 +34,7 @@ const useGenerateConvo = ({
const rootConvo = useRecoilValue(store.conversationByKeySelector(rootIndex)); const rootConvo = useRecoilValue(store.conversationByKeySelector(rootIndex));
useEffect(() => { useEffect(() => {
if (rootConvo?.conversationId && setConversation) { if (rootConvo?.conversationId != null && setConversation) {
setConversation((prevState) => { setConversation((prevState) => {
if (!prevState) { if (!prevState) {
return prevState; return prevState;
@ -85,11 +87,11 @@ const useGenerateConvo = ({
} }
const isAssistantEndpoint = isAssistantsEndpoint(defaultEndpoint); const isAssistantEndpoint = isAssistantsEndpoint(defaultEndpoint);
const assistants: AssistantListItem[] = assistantsListMap[defaultEndpoint] ?? []; const assistants: AssistantListItem[] = assistantsListMap[defaultEndpoint ?? ''] ?? [];
if ( if (
conversation.assistant_id && conversation.assistant_id &&
!assistantsListMap[defaultEndpoint]?.[conversation.assistant_id] !assistantsListMap[defaultEndpoint ?? '']?.[conversation.assistant_id]
) { ) {
conversation.assistant_id = undefined; conversation.assistant_id = undefined;
} }
@ -101,7 +103,7 @@ const useGenerateConvo = ({
} }
if ( if (
conversation.assistant_id && conversation.assistant_id != null &&
isAssistantEndpoint && isAssistantEndpoint &&
conversation.conversationId === 'new' conversation.conversationId === 'new'
) { ) {
@ -109,19 +111,19 @@ const useGenerateConvo = ({
conversation.model = assistant?.model; conversation.model = assistant?.model;
} }
if (conversation.assistant_id && !isAssistantEndpoint) { if (conversation.assistant_id != null && !isAssistantEndpoint) {
conversation.assistant_id = undefined; conversation.assistant_id = undefined;
} }
const models = modelsConfig?.[defaultEndpoint] ?? []; const models = modelsConfig?.[defaultEndpoint ?? ''] ?? [];
conversation = buildDefaultConvo({ conversation = buildDefaultConvo({
conversation, conversation,
lastConversationSetup: preset as TConversation, lastConversationSetup: preset as TConversation,
endpoint: defaultEndpoint, endpoint: defaultEndpoint ?? ('' as EModelEndpoint),
models, models,
}); });
if (preset?.title) { if (preset?.title != null && preset.title !== '') {
conversation.title = preset.title; conversation.title = preset.title;
} }

View file

@ -1,7 +1,7 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { getResponseSender } from 'librechat-data-provider'; import { getResponseSender } from 'librechat-data-provider';
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import type { TEndpointOption, TEndpointsConfig } from 'librechat-data-provider'; import type { TEndpointOption, TEndpointsConfig } from 'librechat-data-provider';
import { useGetEndpointsQuery } from '~/data-provider';
export default function useGetSender() { export default function useGetSender() {
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery(); const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();

View file

@ -1,10 +1,9 @@
import { useEffect, useState, useCallback } from 'react'; import { useEffect, useState, useCallback } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useNavigate, useLocation } from 'react-router-dom'; import { useNavigate, useLocation } from 'react-router-dom';
import { useGetSearchEnabledQuery } from 'librechat-data-provider/react-query';
import type { UseInfiniteQueryResult } from '@tanstack/react-query'; import type { UseInfiniteQueryResult } from '@tanstack/react-query';
import type { ConversationListResponse } from 'librechat-data-provider'; import type { ConversationListResponse } from 'librechat-data-provider';
import { useSearchInfiniteQuery } from '~/data-provider'; import { useSearchInfiniteQuery, useGetSearchEnabledQuery } from '~/data-provider';
import useNewConvo from '~/hooks/useNewConvo'; import useNewConvo from '~/hooks/useNewConvo';
import store from '~/store'; import store from '~/store';

View file

@ -1,9 +1,5 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { import { useGetModelsQuery } from 'librechat-data-provider/react-query';
useGetModelsQuery,
useGetStartupConfig,
useGetEndpointsQuery,
} from 'librechat-data-provider/react-query';
import { import {
alternateName, alternateName,
EModelEndpoint, EModelEndpoint,
@ -13,8 +9,13 @@ import {
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import type { TAssistantsMap, TEndpointsConfig } from 'librechat-data-provider'; import type { TAssistantsMap, TEndpointsConfig } from 'librechat-data-provider';
import type { MentionOption } from '~/common'; import type { MentionOption } from '~/common';
import {
useGetPresetsQuery,
useGetEndpointsQuery,
useListAgentsQuery,
useGetStartupConfig,
} from '~/data-provider';
import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap'; import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap';
import { useGetPresetsQuery, useListAgentsQuery } from '~/data-provider';
import { mapEndpoints, getPresetTitle } from '~/utils'; import { mapEndpoints, getPresetTitle } from '~/utils';
import { EndpointIcon } from '~/components/Endpoints'; import { EndpointIcon } from '~/components/Endpoints';

View file

@ -1,5 +1,5 @@
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
import { useChatContext } from '~/Providers/ChatContext'; import { useChatContext } from '~/Providers/ChatContext';
import { useGetEndpointsQuery } from '~/data-provider';
import { getEndpointField } from '~/utils'; import { getEndpointField } from '~/utils';
import useUserKey from './useUserKey'; import useUserKey from './useUserKey';

View file

@ -1,10 +1,7 @@
import { useMemo, useCallback } from 'react'; import { useMemo, useCallback } from 'react';
import { EModelEndpoint } from 'librechat-data-provider'; import { EModelEndpoint } from 'librechat-data-provider';
import { import { useUserKeyQuery, useUpdateUserKeysMutation } from 'librechat-data-provider/react-query';
useUserKeyQuery, import { useGetEndpointsQuery } from '~/data-provider';
useGetEndpointsQuery,
useUpdateUserKeysMutation,
} from 'librechat-data-provider/react-query';
const useUserKey = (endpoint: string) => { const useUserKey = (endpoint: string) => {
const { data: endpointsConfig } = useGetEndpointsQuery(); const { data: endpointsConfig } = useGetEndpointsQuery();
@ -24,7 +21,7 @@ const useUserKey = (endpoint: string) => {
const getExpiry = useCallback(() => { const getExpiry = useCallback(() => {
if (checkUserKey.data) { if (checkUserKey.data) {
return checkUserKey.data?.expiresAt || 'never'; return checkUserKey.data.expiresAt || 'never';
} }
}, [checkUserKey.data]); }, [checkUserKey.data]);

View file

@ -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 { import {
request,
/* @ts-ignore */ /* @ts-ignore */
createPayload, createPayload,
isAgentsEndpoint, isAgentsEndpoint,
isAssistantsEndpoint,
removeNullishValues, removeNullishValues,
request, isAssistantsEndpoint,
} from 'librechat-data-provider'; } from 'librechat-data-provider';
import { useGetStartupConfig, useGetUserBalance } from 'librechat-data-provider/react-query'; import type { EventSubmission, TMessage, TPayload, TSubmission } from 'librechat-data-provider';
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 { EventHandlerParams } from './useEventHandlers'; 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 useEventHandlers from './useEventHandlers';
import store from '~/store';
type ChatHelpers = Pick< type ChatHelpers = Pick<
EventHandlerParams, EventHandlerParams,

View file

@ -1,9 +1,5 @@
import { useCallback, useRef } from 'react'; import { useCallback, useRef } from 'react';
import { import { useGetModelsQuery } from 'librechat-data-provider/react-query';
useGetModelsQuery,
useGetStartupConfig,
useGetEndpointsQuery,
} from 'librechat-data-provider/react-query';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import {
Constants, Constants,
@ -30,8 +26,8 @@ import {
getModelSpecIconURL, getModelSpecIconURL,
updateLastSelectedModel, updateLastSelectedModel,
} from '~/utils'; } from '~/utils';
import { useDeleteFilesMutation, useGetEndpointsQuery, useGetStartupConfig } from '~/data-provider';
import useAssistantListMap from './Assistants/useAssistantListMap'; import useAssistantListMap from './Assistants/useAssistantListMap';
import { useDeleteFilesMutation } from '~/data-provider';
import { usePauseGlobalAudio } from './Audio'; import { usePauseGlobalAudio } from './Audio';
import { mainTextareaId } from '~/common'; import { mainTextareaId } from '~/common';
import store from '~/store'; import store from '~/store';

View file

@ -1,14 +1,15 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { Constants, EModelEndpoint } from 'librechat-data-provider'; import { Constants, EModelEndpoint } from 'librechat-data-provider';
import { import { useGetModelsQuery } from 'librechat-data-provider/react-query';
useGetModelsQuery,
useGetStartupConfig,
useGetEndpointsQuery,
} from 'librechat-data-provider/react-query';
import type { TPreset } from 'librechat-data-provider'; import type { TPreset } from 'librechat-data-provider';
import {
useGetConvoIdQuery,
useHealthCheck,
useGetEndpointsQuery,
useGetStartupConfig,
} from '~/data-provider';
import { useNewConvo, useAppStartup, useAssistantListMap } from '~/hooks'; import { useNewConvo, useAppStartup, useAssistantListMap } from '~/hooks';
import { useGetConvoIdQuery, useHealthCheck } from '~/data-provider';
import { getDefaultModelSpec, getModelSpecIconURL } from '~/utils'; import { getDefaultModelSpec, getModelSpecIconURL } from '~/utils';
import { ToolCallsMapProvider } from '~/Providers'; import { ToolCallsMapProvider } from '~/Providers';
import ChatView from '~/components/Chat/ChatView'; import ChatView from '~/components/Chat/ChatView';

View file

@ -1,7 +1,23 @@
import { useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { useAuthContext } from '~/hooks/AuthContext'; import { useAuthContext } from '~/hooks/AuthContext';
import StartupLayout from './Startup'; import StartupLayout from './Startup';
import store from '~/store';
export default function LoginLayout() { export default function LoginLayout() {
const { isAuthenticated } = useAuthContext(); const { isAuthenticated } = useAuthContext();
const [queriesEnabled, setQueriesEnabled] = useRecoilState<boolean>(store.queriesEnabled);
useEffect(() => {
if (queriesEnabled) {
return;
}
const timeout: NodeJS.Timeout = setTimeout(() => {
setQueriesEnabled(true);
}, 500);
return () => {
clearTimeout(timeout);
};
}, [queriesEnabled, setQueriesEnabled]);
return <StartupLayout isAuthenticated={isAuthenticated} />; return <StartupLayout isAuthenticated={isAuthenticated} />;
} }

View file

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Outlet, useNavigate, useLocation } from 'react-router-dom'; import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import type { TStartupConfig } from 'librechat-data-provider'; import type { TStartupConfig } from 'librechat-data-provider';
import { useGetStartupConfig } from '~/data-provider';
import AuthLayout from '~/components/Auth/AuthLayout'; import AuthLayout from '~/components/Auth/AuthLayout';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';

View file

@ -1,6 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { Outlet, useNavigate } from 'react-router-dom'; import { Outlet, useNavigate } from 'react-router-dom';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import type { ContextType } from '~/common'; import type { ContextType } from '~/common';
import { import {
AgentsMapContext, AgentsMapContext,
@ -11,7 +10,7 @@ import {
} from '~/Providers'; } from '~/Providers';
import { useAuthContext, useAssistantsMap, useAgentsMap, useFileMap, useSearch } from '~/hooks'; import { useAuthContext, useAssistantsMap, useAgentsMap, useFileMap, useSearch } from '~/hooks';
import TermsAndConditionsModal from '~/components/ui/TermsAndConditionsModal'; import TermsAndConditionsModal from '~/components/ui/TermsAndConditionsModal';
import { useUserTermsQuery } from '~/data-provider'; import { useUserTermsQuery, useGetStartupConfig } from '~/data-provider';
import { Nav, MobileNav } from '~/components/Nav'; import { Nav, MobileNav } from '~/components/Nav';
import { Banner } from '~/components/Banners'; import { Banner } from '~/components/Banners';

View file

@ -9,4 +9,9 @@ const messageAttachmentsMap = atom<Record<string, TAttachment[] | undefined>>({
default: {}, default: {},
}); });
export default { hideBannerHint, messageAttachmentsMap }; const queriesEnabled = atom<boolean>({
key: 'queriesEnabled',
default: true,
});
export default { hideBannerHint, messageAttachmentsMap, queriesEnabled };

View file

@ -133,11 +133,11 @@ export const updateTokenCount = (text: string) => {
return request.post(endpoints.tokenizer(), { arg: text }); return request.post(endpoints.tokenizer(), { arg: text });
}; };
export const login = (payload: t.TLoginUser) => { export const login = (payload: t.TLoginUser): Promise<t.TLoginResponse> => {
return request.post(endpoints.login(), payload); return request.post(endpoints.login(), payload);
}; };
export const logout = () => { export const logout = (): Promise<m.TLogoutResponse> => {
return request.post(endpoints.logout()); return request.post(endpoints.logout());
}; };

View file

@ -53,7 +53,9 @@ export enum MutationKeys {
fileDelete = 'fileDelete', fileDelete = 'fileDelete',
updatePreset = 'updatePreset', updatePreset = 'updatePreset',
deletePreset = 'deletePreset', deletePreset = 'deletePreset',
loginUser = 'loginUser',
logoutUser = 'logoutUser', logoutUser = 'logoutUser',
refreshToken = 'refreshToken',
avatarUpload = 'avatarUpload', avatarUpload = 'avatarUpload',
speechToText = 'speechToText', speechToText = 'speechToText',
textToSpeech = 'textToSpeech', textToSpeech = 'textToSpeech',

View file

@ -5,12 +5,10 @@ import type {
QueryObserverResult, QueryObserverResult,
} from '@tanstack/react-query'; } from '@tanstack/react-query';
import { initialModelsConfig } from '../config'; import { initialModelsConfig } from '../config';
import type { TStartupConfig } from '../config';
import { defaultOrderQuery } from '../types/assistants'; import { defaultOrderQuery } from '../types/assistants';
import * as dataService from '../data-service'; import * as dataService from '../data-service';
import * as m from '../types/mutations'; import * as m from '../types/mutations';
import { QueryKeys } from '../keys'; import { QueryKeys } from '../keys';
import request from '../request';
import * as s from '../schemas'; import * as s from '../schemas';
import * as t from '../types'; import * as t from '../types';
@ -31,18 +29,6 @@ export const useAbortRequestWithMessage = (): UseMutationResult<
); );
}; };
export const useGetUserQuery = (
config?: UseQueryOptions<t.TUser>,
): QueryObserverResult<t.TUser> => {
return useQuery<t.TUser>([QueryKeys.user], () => dataService.getUser(), {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
retry: false,
...config,
});
};
export const useGetMessagesByConvoId = <TData = s.TMessage[]>( export const useGetMessagesByConvoId = <TData = s.TMessage[]>(
id: string, id: string,
config?: UseQueryOptions<s.TMessage[], unknown, TData>, config?: UseQueryOptions<s.TMessage[], unknown, TData>,
@ -98,17 +84,6 @@ export const useGetSharedLinkQuery = (
); );
}; };
export const useGetUserBalance = (
config?: UseQueryOptions<string>,
): QueryObserverResult<string> => {
return useQuery<string>([QueryKeys.balance], () => dataService.getUserBalance(), {
refetchOnWindowFocus: true,
refetchOnReconnect: true,
refetchOnMount: true,
...config,
});
};
export const useGetConversationByIdQuery = ( export const useGetConversationByIdQuery = (
id: string, id: string,
config?: UseQueryOptions<s.TConversation>, config?: UseQueryOptions<s.TConversation>,
@ -226,33 +201,6 @@ export const useRevokeAllUserKeysMutation = (): UseMutationResult<unknown> => {
}); });
}; };
export const useGetSearchEnabledQuery = (
config?: UseQueryOptions<boolean>,
): QueryObserverResult<boolean> => {
return useQuery<boolean>([QueryKeys.searchEnabled], () => dataService.getSearchEnabled(), {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
...config,
});
};
export const useGetEndpointsQuery = <TData = t.TEndpointsConfig>(
config?: UseQueryOptions<t.TEndpointsConfig, unknown, TData>,
): QueryObserverResult<TData> => {
return useQuery<t.TEndpointsConfig, unknown, TData>(
[QueryKeys.endpoints],
() => dataService.getAIEndpoints(),
{
staleTime: Infinity,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
...config,
},
);
};
export const useGetModelsQuery = ( export const useGetModelsQuery = (
config?: UseQueryOptions<t.TModelsConfig>, config?: UseQueryOptions<t.TModelsConfig>,
): QueryObserverResult<t.TModelsConfig> => { ): QueryObserverResult<t.TModelsConfig> => {
@ -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 = ( export const useUserKeyQuery = (
name: string, name: string,
config?: UseQueryOptions<t.TCheckUserKeyResponse>, config?: UseQueryOptions<t.TCheckUserKeyResponse>,
@ -428,17 +362,6 @@ export const useUpdateUserPluginsMutation = (
}); });
}; };
export const useGetStartupConfig = (
config?: UseQueryOptions<TStartupConfig>,
): QueryObserverResult<TStartupConfig> => {
return useQuery<TStartupConfig>([QueryKeys.startupConfig], () => dataService.getStartupConfig(), {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
...config,
});
};
export const useGetCustomConfigSpeechQuery = ( export const useGetCustomConfigSpeechQuery = (
config?: UseQueryOptions<t.TCustomConfigSpeechResponse>, config?: UseQueryOptions<t.TCustomConfigSpeechResponse>,
): QueryObserverResult<t.TCustomConfigSpeechResponse> => { ): QueryObserverResult<t.TCustomConfigSpeechResponse> => {
@ -453,14 +376,3 @@ export const useGetCustomConfigSpeechQuery = (
}, },
); );
}; };
export const useGetBannerQuery = (
config?: UseQueryOptions<t.TBannerResponse>,
): QueryObserverResult<t.TBannerResponse> => {
return useQuery<t.TBannerResponse>([QueryKeys.banner], () => dataService.getBanner(), {
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: false,
...config,
});
};

View file

@ -91,6 +91,10 @@ axios.interceptors.response.use(
return Promise.reject(error); return Promise.reject(error);
} }
if (originalRequest.url?.includes('/api/auth/logout') === true) {
return Promise.reject(error);
}
if (error.response.status === 401 && !originalRequest._retry) { if (error.response.status === 401 && !originalRequest._retry) {
console.warn('401 error, refreshing token'); console.warn('401 error, refreshing token');
originalRequest._retry = true; originalRequest._retry = true;

View file

@ -49,8 +49,6 @@ export type UpdatePresetOptions = MutationOptions<types.TPreset, types.TPreset>;
export type DeletePresetOptions = MutationOptions<PresetDeleteResponse, types.TPreset | undefined>; export type DeletePresetOptions = MutationOptions<PresetDeleteResponse, types.TPreset | undefined>;
export type LogoutOptions = MutationOptions<unknown, undefined>;
/* Assistant mutations */ /* Assistant mutations */
export type AssistantAvatarVariables = { export type AssistantAvatarVariables = {
@ -331,3 +329,10 @@ export type EditArtifactOptions = MutationOptions<
unknown, unknown,
Error Error
>; >;
export type TLogoutResponse = {
message: string;
redirect?: string;
};
export type LogoutOptions = MutationOptions<TLogoutResponse, undefined>;