mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 10:20:15 +01:00
🌐 feat: Add support to SubDirectory hosting (#9155)
* feat: Add support to SubDirectory hosting * fix: address linting and failing test * fix: browser context validation --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
a820863e8b
commit
18d5a75cdc
14 changed files with 252 additions and 199 deletions
|
|
@ -62,7 +62,7 @@ export default () => (
|
|||
<ScreenshotProvider>
|
||||
<App />
|
||||
<iframe
|
||||
src="/assets/silence.mp3"
|
||||
src="assets/silence.mp3"
|
||||
allow="autoplay"
|
||||
id="audio"
|
||||
title="audio-silence"
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ function AuthLayout({
|
|||
<BlinkAnimation active={isFetching}>
|
||||
<div className="mt-6 h-10 w-full bg-cover">
|
||||
<img
|
||||
src="/assets/logo.svg"
|
||||
src="assets/logo.svg"
|
||||
className="h-full w-full object-contain"
|
||||
alt={localize('com_ui_logo', { 0: startupConfig?.appTitle ?? 'LibreChat' })}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { memo, useMemo, useRef, useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useToastContext } from '@librechat/client';
|
||||
import { PermissionTypes, Permissions } from 'librechat-data-provider';
|
||||
import { PermissionTypes, Permissions, dataService } from 'librechat-data-provider';
|
||||
import CodeBlock from '~/components/Messages/Content/CodeBlock';
|
||||
import useHasAccess from '~/hooks/Roles/useHasAccess';
|
||||
import { useFileDownload } from '~/data-provider';
|
||||
|
|
@ -135,9 +135,15 @@ export const a: React.ElementType = memo(({ href, children }: TAnchorProps) => {
|
|||
props.onClick = handleDownload;
|
||||
props.target = '_blank';
|
||||
|
||||
const domainServerBaseUrl = dataService.getDomainServerBaseUrl();
|
||||
|
||||
return (
|
||||
<a
|
||||
href={filepath?.startsWith('files/') ? `/api/${filepath}` : `/api/files/${filepath}`}
|
||||
href={
|
||||
filepath?.startsWith('files/')
|
||||
? `${domainServerBaseUrl}/${filepath}`
|
||||
: `${domainServerBaseUrl}/files/${filepath}`
|
||||
}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default function ApiKeyDialog({
|
|||
{languageIcons.map((icon) => (
|
||||
<div key={icon} className="h-6 w-6">
|
||||
<img
|
||||
src={`/assets/${icon}`}
|
||||
src={`assets/${icon}`}
|
||||
alt=""
|
||||
className="h-full w-full object-contain opacity-[0.85] dark:invert"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -5,24 +5,24 @@ import { IconContext } from '~/common';
|
|||
import { cn } from '~/utils';
|
||||
|
||||
const knownEndpointAssets = {
|
||||
[KnownEndpoints.anyscale]: '/assets/anyscale.png',
|
||||
[KnownEndpoints.apipie]: '/assets/apipie.png',
|
||||
[KnownEndpoints.cohere]: '/assets/cohere.png',
|
||||
[KnownEndpoints.deepseek]: '/assets/deepseek.svg',
|
||||
[KnownEndpoints.fireworks]: '/assets/fireworks.png',
|
||||
[KnownEndpoints.google]: '/assets/google.svg',
|
||||
[KnownEndpoints.groq]: '/assets/groq.png',
|
||||
[KnownEndpoints.huggingface]: '/assets/huggingface.svg',
|
||||
[KnownEndpoints.mistral]: '/assets/mistral.png',
|
||||
[KnownEndpoints.mlx]: '/assets/mlx.png',
|
||||
[KnownEndpoints.ollama]: '/assets/ollama.png',
|
||||
[KnownEndpoints.openai]: '/assets/openai.svg',
|
||||
[KnownEndpoints.openrouter]: '/assets/openrouter.png',
|
||||
[KnownEndpoints.perplexity]: '/assets/perplexity.png',
|
||||
[KnownEndpoints.qwen]: '/assets/qwen.svg',
|
||||
[KnownEndpoints.shuttleai]: '/assets/shuttleai.png',
|
||||
[KnownEndpoints['together.ai']]: '/assets/together.png',
|
||||
[KnownEndpoints.unify]: '/assets/unify.webp',
|
||||
[KnownEndpoints.anyscale]: 'assets/anyscale.png',
|
||||
[KnownEndpoints.apipie]: 'assets/apipie.png',
|
||||
[KnownEndpoints.cohere]: 'assets/cohere.png',
|
||||
[KnownEndpoints.deepseek]: 'assets/deepseek.svg',
|
||||
[KnownEndpoints.fireworks]: 'assets/fireworks.png',
|
||||
[KnownEndpoints.google]: 'assets/google.svg',
|
||||
[KnownEndpoints.groq]: 'assets/groq.png',
|
||||
[KnownEndpoints.huggingface]: 'assets/huggingface.svg',
|
||||
[KnownEndpoints.mistral]: 'assets/mistral.png',
|
||||
[KnownEndpoints.mlx]: 'assets/mlx.png',
|
||||
[KnownEndpoints.ollama]: 'assets/ollama.png',
|
||||
[KnownEndpoints.openai]: 'assets/openai.svg',
|
||||
[KnownEndpoints.openrouter]: 'assets/openrouter.png',
|
||||
[KnownEndpoints.perplexity]: 'assets/perplexity.png',
|
||||
[KnownEndpoints.qwen]: 'assets/qwen.svg',
|
||||
[KnownEndpoints.shuttleai]: 'assets/shuttleai.png',
|
||||
[KnownEndpoints['together.ai']]: 'assets/together.png',
|
||||
[KnownEndpoints.unify]: 'assets/unify.webp',
|
||||
};
|
||||
|
||||
const knownEndpointClasses = {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export default function useAttachmentHandler(queryClient?: QueryClient) {
|
|||
return ({ data }: { data: TAttachment; submission: EventSubmission }) => {
|
||||
const { messageId } = data;
|
||||
|
||||
if (queryClient && data?.filepath && !data.filepath.startsWith('/api/files')) {
|
||||
if (queryClient && data?.filepath && !data.filepath.includes('/api/files')) {
|
||||
queryClient.setQueryData([QueryKeys.files], (oldData: TAttachment[] | undefined) => {
|
||||
return [data, ...(oldData || [])];
|
||||
});
|
||||
|
|
|
|||
|
|
@ -27,95 +27,101 @@ const AuthLayout = () => (
|
|||
</AuthContextProvider>
|
||||
);
|
||||
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
path: 'share/:shareId',
|
||||
element: <ShareRoute />,
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
},
|
||||
{
|
||||
path: 'oauth',
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
path: 'success',
|
||||
element: <OAuthSuccess />,
|
||||
},
|
||||
{
|
||||
path: 'error',
|
||||
element: <OAuthError />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
element: <StartupLayout />,
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
path: 'register',
|
||||
element: <Registration />,
|
||||
},
|
||||
{
|
||||
path: 'forgot-password',
|
||||
element: <RequestPasswordReset />,
|
||||
},
|
||||
{
|
||||
path: 'reset-password',
|
||||
element: <ResetPassword />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'verify',
|
||||
element: <VerifyEmail />,
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
},
|
||||
{
|
||||
element: <AuthLayout />,
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
path: '/',
|
||||
element: <LoginLayout />,
|
||||
children: [
|
||||
{
|
||||
path: 'login',
|
||||
element: <Login />,
|
||||
},
|
||||
{
|
||||
path: 'login/2fa',
|
||||
element: <TwoFactorScreen />,
|
||||
},
|
||||
],
|
||||
},
|
||||
dashboardRoutes,
|
||||
{
|
||||
path: '/',
|
||||
element: <Root />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Navigate to="/c/new" replace={true} />,
|
||||
},
|
||||
{
|
||||
path: 'c/:conversationId?',
|
||||
element: <ChatRoute />,
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
element: <Search />,
|
||||
},
|
||||
{
|
||||
path: 'agents',
|
||||
element: <AgentMarketplace />,
|
||||
},
|
||||
{
|
||||
path: 'agents/:category',
|
||||
element: <AgentMarketplace />,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const baseEl = document.querySelector('base');
|
||||
const baseHref = baseEl?.getAttribute('href') || '/';
|
||||
|
||||
export const router = createBrowserRouter(
|
||||
[
|
||||
{
|
||||
path: 'share/:shareId',
|
||||
element: <ShareRoute />,
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
},
|
||||
{
|
||||
path: 'oauth',
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
path: 'success',
|
||||
element: <OAuthSuccess />,
|
||||
},
|
||||
{
|
||||
path: 'error',
|
||||
element: <OAuthError />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
element: <StartupLayout />,
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
path: 'register',
|
||||
element: <Registration />,
|
||||
},
|
||||
{
|
||||
path: 'forgot-password',
|
||||
element: <RequestPasswordReset />,
|
||||
},
|
||||
{
|
||||
path: 'reset-password',
|
||||
element: <ResetPassword />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'verify',
|
||||
element: <VerifyEmail />,
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
},
|
||||
{
|
||||
element: <AuthLayout />,
|
||||
errorElement: <RouteErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
path: '/',
|
||||
element: <LoginLayout />,
|
||||
children: [
|
||||
{
|
||||
path: 'login',
|
||||
element: <Login />,
|
||||
},
|
||||
{
|
||||
path: 'login/2fa',
|
||||
element: <TwoFactorScreen />,
|
||||
},
|
||||
],
|
||||
},
|
||||
dashboardRoutes,
|
||||
{
|
||||
path: '/',
|
||||
element: <Root />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Navigate to="/c/new" replace={true} />,
|
||||
},
|
||||
{
|
||||
path: 'c/:conversationId?',
|
||||
element: <ChatRoute />,
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
element: <Search />,
|
||||
},
|
||||
{
|
||||
path: 'agents',
|
||||
element: <AgentMarketplace />,
|
||||
},
|
||||
{
|
||||
path: 'agents/:category',
|
||||
element: <AgentMarketplace />,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
{ basename: baseHref },
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue