diff --git a/packages/global/common/file/constants.ts b/packages/global/common/file/constants.ts
index 9cc2201e1..4be4bab22 100644
--- a/packages/global/common/file/constants.ts
+++ b/packages/global/common/file/constants.ts
@@ -16,7 +16,7 @@ export const bucketNameMap = {
}
};
-export const ReadFileBaseUrl = `${process.env.FE_DOMAIN || ''}/api/common/file/read`;
+export const ReadFileBaseUrl = `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL}/api/common/file/read`;
export const documentFileType = '.txt, .docx, .csv, .xlsx, .pdf, .md, .html, .pptx';
export const imageFileType =
diff --git a/packages/global/core/chat/adapt.ts b/packages/global/core/chat/adapt.ts
index 9b9c34d32..4b4b156d4 100644
--- a/packages/global/core/chat/adapt.ts
+++ b/packages/global/core/chat/adapt.ts
@@ -14,7 +14,6 @@ import type {
ChatCompletionToolMessageParam
} from '../../core/ai/type.d';
import { ChatCompletionRequestMessageRoleEnum } from '../../core/ai/constants';
-
const GPT2Chat = {
[ChatCompletionRequestMessageRoleEnum.System]: ChatRoleEnum.System,
[ChatCompletionRequestMessageRoleEnum.User]: ChatRoleEnum.Human,
@@ -61,14 +60,14 @@ export const chats2GPTMessages = ({
return {
type: 'image_url',
image_url: {
- url: item.file?.url || ''
+ url: item.file.url
}
};
} else if (item.file?.type === ChatFileTypeEnum.file) {
return {
type: 'file_url',
name: item.file?.name || '',
- url: item.file?.url || ''
+ url: item.file.url
};
}
}
diff --git a/packages/web/common/system/utils.ts b/packages/web/common/system/utils.ts
index be42ee812..1015f699f 100644
--- a/packages/web/common/system/utils.ts
+++ b/packages/web/common/system/utils.ts
@@ -9,3 +9,12 @@ export const getUserFingerprint = async () => {
export const hasHttps = () => {
return window.location.protocol === 'https:';
};
+
+export const getWebReqUrl = (url: string = '') => {
+ if (!url) return '/';
+ const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
+ if (!baseUrl) return url;
+
+ if (!url.startsWith('/') || url.startsWith(baseUrl)) return url;
+ return `${baseUrl}${url}`;
+};
diff --git a/packages/web/components/common/Avatar/index.tsx b/packages/web/components/common/Avatar/index.tsx
index 141f20e47..9f398ac22 100644
--- a/packages/web/components/common/Avatar/index.tsx
+++ b/packages/web/components/common/Avatar/index.tsx
@@ -1,9 +1,10 @@
import React from 'react';
-import { Box, Flex, Image } from '@chakra-ui/react';
+import { Box } from '@chakra-ui/react';
import type { ImageProps } from '@chakra-ui/react';
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
import MyIcon from '../Icon';
import { iconPaths } from '../Icon/constants';
+import MyImage from '../Image/MyImage';
const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
// @ts-ignore
@@ -14,7 +15,7 @@ const Avatar = ({ w = '30px', src, ...props }: ImageProps) => {
) : (
- {
+ return ;
+};
+export default React.memo(MyImage);
diff --git a/packages/web/components/common/Image/PhotoView.tsx b/packages/web/components/common/Image/PhotoView.tsx
index 5d2199aa4..33cbdd615 100644
--- a/packages/web/components/common/Image/PhotoView.tsx
+++ b/packages/web/components/common/Image/PhotoView.tsx
@@ -1,9 +1,10 @@
import React from 'react';
import { PhotoProvider, PhotoView } from 'react-photo-view';
import 'react-photo-view/dist/react-photo-view.css';
-import { Box, Image, ImageProps } from '@chakra-ui/react';
+import { ImageProps } from '@chakra-ui/react';
import { useSystem } from '../../../hooks/useSystem';
import Loading from '../MyLoading';
+import MyImage from './MyImage';
const MyPhotoView = ({ ...props }: ImageProps) => {
const { isPc } = useSystem();
@@ -15,7 +16,7 @@ const MyPhotoView = ({ ...props }: ImageProps) => {
loadingElement={}
>
-
+
);
diff --git a/packages/web/components/common/MyDrawer/CustomRightDrawer.tsx b/packages/web/components/common/MyDrawer/CustomRightDrawer.tsx
index 345673d5c..8d590a251 100644
--- a/packages/web/components/common/MyDrawer/CustomRightDrawer.tsx
+++ b/packages/web/components/common/MyDrawer/CustomRightDrawer.tsx
@@ -1,7 +1,7 @@
import React from 'react';
-import MyIcon from '../Icon';
-import { Flex, Image, Box, CloseButton, FlexProps } from '@chakra-ui/react';
+import { Flex, Box, CloseButton, FlexProps } from '@chakra-ui/react';
import { useLoading } from '../../../hooks/useLoading';
+import Avatar from '../Avatar';
type Props = FlexProps & {
onClose: () => void;
@@ -50,15 +50,7 @@ const CustomRightDrawer = ({
py={'10px'}
px={5}
>
- {iconSrc && (
- <>
- {iconSrc.startsWith('/') ? (
-
- ) : (
-
- )}
- >
- )}
+ {iconSrc && }
{title}
diff --git a/packages/web/components/common/MyDrawer/MyRightDrawer.tsx b/packages/web/components/common/MyDrawer/MyRightDrawer.tsx
index 94bb01232..7dc3c428b 100644
--- a/packages/web/components/common/MyDrawer/MyRightDrawer.tsx
+++ b/packages/web/components/common/MyDrawer/MyRightDrawer.tsx
@@ -13,6 +13,7 @@ import {
Box
} from '@chakra-ui/react';
import { useLoading } from '../../../hooks/useLoading';
+import Avatar from '../Avatar';
type Props = DrawerContentProps & {
onClose: () => void;
@@ -52,15 +53,7 @@ const MyRightDrawer = ({
py={'10px'}
px={5}
>
- {iconSrc && (
- <>
- {iconSrc.startsWith('/') ? (
-
- ) : (
-
- )}
- >
- )}
+ {iconSrc && }
{title}
diff --git a/packages/web/components/common/Textarea/CodeEditor/Editor.tsx b/packages/web/components/common/Textarea/CodeEditor/Editor.tsx
index 49fb0ba7a..4177ef897 100644
--- a/packages/web/components/common/Textarea/CodeEditor/Editor.tsx
+++ b/packages/web/components/common/Textarea/CodeEditor/Editor.tsx
@@ -2,9 +2,10 @@ import React, { useCallback, useRef, useState } from 'react';
import Editor, { Monaco, loader } from '@monaco-editor/react';
import { Box, BoxProps } from '@chakra-ui/react';
import MyIcon from '../../Icon';
+import { getWebReqUrl } from '../../../../common/system/utils';
loader.config({
- paths: { vs: '/js/monaco-editor.0.45.0/vs' }
+ paths: { vs: getWebReqUrl('/js/monaco-editor.0.45.0/vs') }
});
type EditorVariablePickerType = {
diff --git a/packages/web/components/common/Textarea/JsonEditor/index.tsx b/packages/web/components/common/Textarea/JsonEditor/index.tsx
index 18c70163c..feea41284 100644
--- a/packages/web/components/common/Textarea/JsonEditor/index.tsx
+++ b/packages/web/components/common/Textarea/JsonEditor/index.tsx
@@ -4,9 +4,10 @@ import { Box, BoxProps } from '@chakra-ui/react';
import MyIcon from '../../Icon';
import { useToast } from '../../../../hooks/useToast';
import { useTranslation } from 'next-i18next';
+import { getWebReqUrl } from '../../../../common/system/utils';
loader.config({
- paths: { vs: '/js/monaco-editor.0.45.0/vs' }
+ paths: { vs: getWebReqUrl('/js/monaco-editor.0.45.0/vs') }
});
type EditorVariablePickerType = {
diff --git a/packages/web/components/common/Textarea/PromptEditor/modules/ComfirmVar/index.tsx b/packages/web/components/common/Textarea/PromptEditor/modules/ComfirmVar/index.tsx
deleted file mode 100644
index 22c669f27..000000000
--- a/packages/web/components/common/Textarea/PromptEditor/modules/ComfirmVar/index.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { Box, Button, Image } from '@chakra-ui/react';
-import { useTranslation } from 'next-i18next';
-export default function ComfirmVar({
- newVariables,
- onCancel,
- onConfirm
-}: {
- newVariables: string[];
- onCancel: () => void;
- onConfirm: () => void;
-}) {
- const { t } = useTranslation();
- return (
- <>
-
-
-
-
-
-
- {t('common:undefined_var')}
-
-
- {newVariables.map((item, index) => (
-
-
- {`{{`}
- {item}
- {`}}`}
-
-
- ))}
-
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json
index f7040562d..f51568158 100644
--- a/packages/web/i18n/en/common.json
+++ b/packages/web/i18n/en/common.json
@@ -1119,7 +1119,6 @@
"tag_list": "Tag List",
"team_tag": "Team Tag",
"textarea_variable_picker_tip": "Enter \"/\" to select a variable",
- "undefined_var": "Referenced an undefined variable, add it automatically?",
"unit.character": "Character",
"unit.minute": "Minute",
"unusable_variable": "No Usable Variables",
diff --git a/packages/web/i18n/zh/common.json b/packages/web/i18n/zh/common.json
index 1b669e466..9657b3661 100644
--- a/packages/web/i18n/zh/common.json
+++ b/packages/web/i18n/zh/common.json
@@ -1125,7 +1125,6 @@
"tag_list": "标签列表",
"team_tag": "团队标签",
"textarea_variable_picker_tip": "输入\"/\"可选择变量",
- "undefined_var": "引用了未定义的变量,是否自动添加?",
"unit.character": "字符",
"unit.minute": "分钟",
"unusable_variable": "无可用变量",
diff --git a/projects/app/.env.template b/projects/app/.env.template
index c6eba736a..9decedb37 100644
--- a/projects/app/.env.template
+++ b/projects/app/.env.template
@@ -35,6 +35,8 @@ SANDBOX_URL=http://localhost:3001
PRO_URL=
# 页面的地址,用于自动补全相对路径资源的 domain
FE_DOMAIN=http://localhost:3000
+# 二级路由
+# NEXT_PUBLIC_BASE_URL=/fastai
# 日志等级: debug, info, warn, error
LOG_LEVEL=debug
diff --git a/projects/app/Dockerfile b/projects/app/Dockerfile
index 1946263a0..1417f8ae4 100644
--- a/projects/app/Dockerfile
+++ b/projects/app/Dockerfile
@@ -26,6 +26,7 @@ FROM node:20.14.0-alpine AS builder
WORKDIR /app
ARG proxy
+ARG base_url
# copy common node_modules and one project node_modules
COPY package.json pnpm-workspace.yaml .npmrc tsconfig.json ./
@@ -39,6 +40,7 @@ RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /
RUN apk add --no-cache libc6-compat && npm install -g pnpm@9.4.0
ENV NODE_OPTIONS="--max-old-space-size=4096"
+ENV NEXT_PUBLIC_BASE_URL=$base_url
RUN pnpm --filter=app build
# --------- runner -----------
@@ -46,6 +48,7 @@ FROM node:20.14.0-alpine AS runner
WORKDIR /app
ARG proxy
+ARG base_url
# create user and use it
RUN addgroup --system --gid 1001 nodejs
@@ -81,6 +84,7 @@ RUN chown -R nextjs:nodejs /app/data
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
+ENV NEXT_PUBLIC_BASE_URL=$base_url
EXPOSE 3000
diff --git a/projects/app/next.config.js b/projects/app/next.config.js
index 1e455e044..73a53cd4e 100644
--- a/projects/app/next.config.js
+++ b/projects/app/next.config.js
@@ -6,6 +6,7 @@ const isDev = process.env.NODE_ENV === 'development';
/** @type {import('next').NextConfig} */
const nextConfig = {
+ basePath: process.env.NEXT_PUBLIC_BASE_URL,
i18n,
output: 'standalone',
reactStrictMode: isDev ? false : true,
diff --git a/projects/app/src/components/MyImage/index.tsx b/projects/app/src/components/MyImage/index.tsx
index c595a888d..46d49c99d 100644
--- a/projects/app/src/components/MyImage/index.tsx
+++ b/projects/app/src/components/MyImage/index.tsx
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
-import { Image, Skeleton, ImageProps } from '@chakra-ui/react';
+import { Skeleton, ImageProps } from '@chakra-ui/react';
+import CustomImage from '@fastgpt/web/components/common/Image/MyImage';
export const MyImage = (props: ImageProps) => {
const [isLoading, setIsLoading] = useState(true);
@@ -13,7 +14,7 @@ export const MyImage = (props: ImageProps) => {
justifyContent={'center'}
my={1}
>
-
{audioPlaying ? (
-
+
{feConfigs?.systemTitle}
@@ -137,7 +138,7 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
w={'100%'}
h={'40px'}
borderRadius={'sm'}
- leftIcon={}
+ leftIcon={}
onClick={() => {
feConfigs.sso?.url && router.replace(feConfigs.sso?.url, '_self');
}}
diff --git a/projects/app/src/pages/login/index.tsx b/projects/app/src/pages/login/index.tsx
index fcf641482..13510a7fc 100644
--- a/projects/app/src/pages/login/index.tsx
+++ b/projects/app/src/pages/login/index.tsx
@@ -28,6 +28,7 @@ import I18nLngSelector from '@/components/Select/I18nLngSelector';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { GET } from '@/web/common/api/request';
import { getDocPath } from '@/web/common/system/doc';
+import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
const RegisterForm = dynamic(() => import('./components/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm'));
@@ -136,7 +137,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
{
- const response = await fetch(`/docs/${url}`);
+ const response = await fetch(getWebReqUrl(`/docs/${url}`));
const textContent = await response.text();
return textContent;
};
diff --git a/projects/app/src/web/common/system/doc.ts b/projects/app/src/web/common/system/doc.ts
index b64b85881..251350196 100644
--- a/projects/app/src/web/common/system/doc.ts
+++ b/projects/app/src/web/common/system/doc.ts
@@ -1,3 +1,4 @@
+import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import { useSystemStore } from './useSystemStore';
export const getDocPath = (path: string) => {
const feConfigs = useSystemStore.getState().feConfigs;
@@ -5,5 +6,6 @@ export const getDocPath = (path: string) => {
if (!feConfigs?.docUrl) return '';
if (!path.startsWith('/')) return path;
if (feConfigs.docUrl.endsWith('/')) return feConfigs.docUrl.slice(0, -1);
- return feConfigs.docUrl + path;
+
+ return getWebReqUrl(feConfigs.docUrl + path);
};
diff --git a/projects/app/src/web/common/utils/voice.ts b/projects/app/src/web/common/utils/voice.ts
index bbfb4d3b9..46d6f1b64 100644
--- a/projects/app/src/web/common/utils/voice.ts
+++ b/projects/app/src/web/common/utils/voice.ts
@@ -8,6 +8,7 @@ import { TTSTypeEnum } from '@/web/core/app/constants';
import { useTranslation } from 'next-i18next';
import type { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat.d';
import { useMount } from 'ahooks';
+import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
const contentType = 'audio/mpeg';
const splitMarker = 'SPLIT_MARKER';
@@ -45,7 +46,7 @@ export const useAudioPlay = (props?: OutLinkChatAuthProps & { ttsConfig?: AppTTS
setAudioLoading(true);
audioController.current = new AbortController();
- const response = await fetch('/api/core/chat/item/getSpeech', {
+ const response = await fetch(getWebReqUrl('/api/core/chat/item/getSpeech'), {
method: 'POST',
headers: {
'Content-Type': 'application/json'