Feat/startup config api (#518)

* feat: add api for config

* feat: add data service to client

* feat: update client pages with values from config endpoint

* test: update tests

* Update configurations and documentation to remove VITE_SHOW_GOOGLE_LOGIN_OPTION and change VITE_APP_TITLE to APP_TITLE

* include APP_TITLE with startup config

* Add test for new route

* update backend-review pipeline

* comment out test until we can figure out testing routes in CI

* update: .env.example

---------

Co-authored-by: fuegovic <32828263+fuegovic@users.noreply.github.com>
This commit is contained in:
Dan Orlando 2023-06-15 09:36:34 -07:00 committed by GitHub
parent 2da81db440
commit 3634d8691a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 419 additions and 75 deletions

View file

@ -2,10 +2,11 @@ import { useEffect } from 'react';
import LoginForm from './LoginForm';
import { useAuthContext } from '~/hooks/AuthContext';
import { useNavigate } from 'react-router-dom';
import { SHOW_GOOGLE_LOGIN_OPTION, ALLOW_REGISTRATION, DOMAIN_SERVER } from "~/utils/envConstants";
import { useGetStartupConfig } from '~/data-provider';
function Login() {
const { login, error, isAuthenticated } = useAuthContext();
const { data: startupConfig } = useGetStartupConfig();
const navigate = useNavigate();
@ -29,7 +30,7 @@ function Login() {
</div>
)}
<LoginForm onSubmit={login} />
{ALLOW_REGISTRATION && (
{startupConfig?.registrationEnabled && (
<p className="my-4 text-center text-sm font-light text-gray-700">
{' '}
Don&apos;t have an account?{' '}
@ -38,7 +39,7 @@ function Login() {
</a>
</p>
)}
{SHOW_GOOGLE_LOGIN_OPTION && (
{startupConfig?.googleLoginEnabled && (
<>
<div className="relative mt-6 flex w-full items-center justify-center border border-t uppercase">
<div className="absolute bg-white px-3 text-xs">Or</div>
@ -47,7 +48,7 @@ function Login() {
<a
aria-label="Login with Google"
className="justify-left flex w-full items-center space-x-3 rounded-md border border-gray-300 px-5 py-3 hover:bg-gray-50 focus:ring-2 focus:ring-violet-600 focus:ring-offset-1"
href={`${DOMAIN_SERVER}/oauth/google`}
href={`${startupConfig.serverDomain}/oauth/google`}
>
<svg
xmlns="http://www.w3.org/2000/svg"

View file

@ -1,11 +1,11 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { useRegisterUserMutation, TRegisterUser } from '~/data-provider';
import { SHOW_GOOGLE_LOGIN_OPTION, DOMAIN_SERVER } from '~/utils/envConstants';
import { useRegisterUserMutation, TRegisterUser, useGetStartupConfig } from '~/data-provider';
function Registration() {
const navigate = useNavigate();
const { data: startupConfig } = useGetStartupConfig();
const {
register,
@ -34,6 +34,12 @@ function Registration() {
});
};
useEffect(() => {
if (startupConfig?.registrationEnabled === false) {
navigate('/login');
}
}, [startupConfig, navigate]);
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-white pt-6 sm:pt-0">
<div className="mt-6 w-96 overflow-hidden bg-white px-6 py-4 sm:max-w-md sm:rounded-lg">
@ -266,7 +272,7 @@ function Registration() {
Login
</a>
</p>
{SHOW_GOOGLE_LOGIN_OPTION && (
{startupConfig?.googleLoginEnabled && (
<>
<div className="relative mt-6 flex w-full items-center justify-center border border-t uppercase">
<div className="absolute bg-white px-3 text-xs">Or</div>
@ -275,7 +281,7 @@ function Registration() {
<div className="mt-4 flex gap-x-2">
<a
aria-label="Login with Google"
href={`${DOMAIN_SERVER}/oauth/google`}
href={`${startupConfig.serverDomain}/oauth/google`}
className="justify-left flex w-full items-center space-x-3 rounded-md border border-gray-300 px-5 py-3 hover:bg-gray-50 focus:ring-2 focus:ring-violet-600 focus:ring-offset-1"
>
<svg

View file

@ -3,12 +3,6 @@ import userEvent from '@testing-library/user-event';
import Login from '../Login';
import * as mockDataProvider from '~/data-provider';
jest.mock('~/utils/envConstants', () => ({
DOMAIN_SERVER: 'mock-server',
SHOW_GOOGLE_LOGIN_OPTION: true,
ALLOW_REGISTRATION: true
}));
jest.mock('~/data-provider');
const setup = ({
@ -23,6 +17,15 @@ const setup = ({
mutate: jest.fn(),
data: {},
isSuccess: false
},
useGetStartupCongfigReturnValue = {
isLoading: false,
isError: false,
data: {
googleLoginEnabled: true,
registrationEnabled: true,
serverDomain: 'mock-server'
}
}
} = {}) => {
const mockUseLoginUser = jest
@ -33,12 +36,16 @@ const setup = ({
.spyOn(mockDataProvider, 'useGetUserQuery')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetUserQueryReturnValue);
const mockUseGetStartupConfig = jest
.spyOn(mockDataProvider, 'useGetStartupConfig')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetStartupCongfigReturnValue);
const renderResult = render(<Login />);
return {
...renderResult,
mockUseLoginUser,
mockUseGetUserQuery
mockUseGetUserQuery,
mockUseGetStartupConfig
};
};

View file

@ -3,11 +3,6 @@ import userEvent from '@testing-library/user-event';
import Registration from '../Registration';
import * as mockDataProvider from '~/data-provider';
jest.mock('~/utils/envConstants', () => ({
DOMAIN_SERVER: 'mock-server',
SHOW_GOOGLE_LOGIN_OPTION: true
}));
jest.mock('~/data-provider');
const setup = ({
@ -22,6 +17,15 @@ const setup = ({
mutate: jest.fn(),
data: {},
isSuccess: false
},
useGetStartupCongfigReturnValue = {
isLoading: false,
isError: false,
data: {
googleLoginEnabled: true,
registrationEnabled: true,
serverDomain: 'mock-server'
}
}
} = {}) => {
const mockUseRegisterUserMutation = jest
@ -32,13 +36,18 @@ const setup = ({
.spyOn(mockDataProvider, 'useGetUserQuery')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetUserQueryReturnValue);
const mockUseGetStartupConfig = jest
.spyOn(mockDataProvider, 'useGetStartupConfig')
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
.mockReturnValue(useGetStartupCongfigReturnValue);
const renderResult = render(<Registration />);
return {
...renderResult,
mockUseRegisterUserMutation,
mockUseGetUserQuery
mockUseGetUserQuery,
mockUseGetStartupConfig
};
};

View file

@ -1,6 +1,8 @@
import React from 'react';
import { useGetStartupConfig } from '~/data-provider';
export default function Footer() {
const { data: config } = useGetStartupConfig();
return (
<div className="hidden px-3 pb-1 pt-2 text-center text-xs text-black/50 dark:text-white/50 md:block md:px-4 md:pb-4 md:pt-3">
<a
@ -9,7 +11,7 @@ export default function Footer() {
rel="noreferrer"
className="underline"
>
{import.meta.env.VITE_APP_TITLE || 'LibreChat'}
{config?.appTitle || 'LibreChat'}
</a>
. Serves and searches all conversations reliably. All AI convos under one house. Pay per call
and not per month (cents compared to dollars).

View file

@ -1,20 +1,24 @@
import React from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import useDocumentTitle from '~/hooks/useDocumentTitle';
import SunIcon from '../svg/SunIcon';
import LightningIcon from '../svg/LightningIcon';
import CautionIcon from '../svg/CautionIcon';
import store from '~/store';
import { useGetStartupConfig } from '~/data-provider';
export default function Landing() {
const { data: config } = useGetStartupConfig();
const setText = useSetRecoilState(store.text);
const conversation = useRecoilValue(store.conversation);
// @ts-ignore TODO: Fix anti-pattern - requires refactoring conversation store
const { title = 'New Chat' } = conversation || {};
useDocumentTitle(title);
const clickHandler = (e) => {
const clickHandler = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
const { innerText } = e.target;
const { innerText } = e.target as HTMLButtonElement;
const quote = innerText.split('"')[1].trim();
setText(quote);
};
@ -26,7 +30,7 @@ export default function Landing() {
id="landing-title"
className="mb-10 ml-auto mr-auto mt-6 flex items-center justify-center gap-2 text-center text-4xl font-semibold sm:mb-16 md:mt-[10vh]"
>
{import.meta.env.VITE_APP_TITLE || 'LibreChat'}
{config?.appTitle || 'LibreChat'}
</h1>
<div className="items-start gap-3.5 text-center md:flex">
<div className="mb-8 flex flex-1 flex-col gap-3.5 md:mb-auto">