This commit is contained in:
Archer
2023-12-27 11:07:39 +08:00
committed by GitHub
parent 86286efb54
commit 759a2330e6
182 changed files with 3099 additions and 81685 deletions

View File

@@ -1,6 +1,10 @@
import { postUploadImg, postUploadFiles } from '@/web/common/file/api';
import { UploadImgProps } from '@fastgpt/global/common/file/api';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import {
compressBase64ImgAndUpload as compressBase64ImgAndUploadControl,
type CompressImgProps
} from '@fastgpt/web/common/file/img';
/**
* upload file to mongo gridfs
@@ -30,76 +34,40 @@ export const uploadFiles = ({
});
};
export const getUploadMdImgController = ({
base64Img,
metadata
}: {
base64Img: string;
metadata: Record<string, any>;
}) =>
compressBase64ImgAndUpload({
base64Img,
maxW: 4000,
maxH: 4000,
maxSize: 1024 * 1024 * 5,
metadata
});
/**
* compress image. response base64
* @param maxSize The max size of the compressed image
*/
export const compressBase64ImgAndUpload = ({
base64Img,
maxW = 1080,
maxH = 1080,
maxSize = 1024 * 500, // 300kb
expiredTime,
metadata,
shareId
}: UploadImgProps & {
maxW?: number;
maxH?: number;
maxSize?: number;
}) => {
return new Promise<string>((resolve, reject) => {
const fileType =
/^data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,/.exec(base64Img)?.[1] || 'image/jpeg';
const img = new Image();
img.src = base64Img;
img.onload = async () => {
let width = img.width;
let height = img.height;
if (width > height) {
if (width > maxW) {
height *= maxW / width;
width = maxW;
}
} else {
if (height > maxH) {
width *= maxH / height;
height = maxH;
}
}
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) {
return reject('压缩图片异常');
}
ctx.drawImage(img, 0, 0, width, height);
const compressedDataUrl = canvas.toDataURL(fileType, 1);
// 移除 canvas 元素
canvas.remove();
if (compressedDataUrl.length > maxSize) {
return reject('图片太大了');
}
try {
const src = await postUploadImg({
shareId,
base64Img: compressedDataUrl,
expiredTime,
metadata
});
resolve(src);
} catch (error) {
reject(error);
}
};
img.onerror = reject;
shareId,
...props
}: UploadImgProps & CompressImgProps) => {
return compressBase64ImgAndUploadControl({
...props,
uploadController: (base64Img) =>
postUploadImg({
shareId,
base64Img,
expiredTime,
metadata
})
});
};
export const compressImgFileAndUpload = async ({

View File

@@ -3,94 +3,23 @@ import Papa from 'papaparse';
import { compressBase64ImgAndUpload } from './controller';
import { simpleMarkdownText } from '@fastgpt/global/common/string/markdown';
import { htmlStr2Md } from '@fastgpt/web/common/string/markdown';
/**
* 读取 txt 文件内容
*/
export const readTxtContent = (file: File) => {
return new Promise((resolve: (_: string) => void, reject) => {
try {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = (err) => {
console.log('error txt read:', err);
reject('读取 txt 文件失败');
};
reader.readAsText(file);
} catch (error) {
reject('浏览器不支持文件内容读取');
}
});
};
import { readPdfFile } from '@fastgpt/global/common/file/read/index';
import { readFileRawText } from '@fastgpt/web/common/file/read';
/**
* read pdf to raw text
*/
export const readPdfContent = (file: File) =>
new Promise<string>((resolve, reject) => {
type TokenType = {
str: string;
dir: string;
width: number;
height: number;
transform: number[];
fontName: string;
hasEOL: boolean;
};
try {
const pdfjsLib = window['pdfjs-dist/build/pdf'];
pdfjsLib.workerSrc = '/js/pdf.worker.js';
const readPDFPage = async (doc: any, pageNo: number) => {
const page = await doc.getPage(pageNo);
const tokenizedText = await page.getTextContent();
const viewport = page.getViewport({ scale: 1 });
const pageHeight = viewport.height;
const headerThreshold = pageHeight * 0.07; // 假设页头在页面顶部5%的区域内
const footerThreshold = pageHeight * 0.93; // 假设页脚在页面底部5%的区域内
const pageTexts: TokenType[] = tokenizedText.items.filter((token: TokenType) => {
return (
!token.transform ||
(token.transform[5] > headerThreshold && token.transform[5] < footerThreshold)
);
});
// concat empty string 'hasEOL'
for (let i = 0; i < pageTexts.length; i++) {
const item = pageTexts[i];
if (item.str === '' && pageTexts[i - 1]) {
pageTexts[i - 1].hasEOL = item.hasEOL;
pageTexts.splice(i, 1);
i--;
}
}
return pageTexts
.map((token) => {
const paragraphEnd = token.hasEOL && /([。?!.?!\n\r]|(\r\n))$/.test(token.str);
return paragraphEnd ? `${token.str}\n` : token.str;
})
.join('');
};
let reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = async (event) => {
if (!event?.target?.result) return reject('解析 PDF 失败');
try {
const doc = await pdfjsLib.getDocument(event.target.result).promise;
const pageTextPromises = [];
for (let pageNo = 1; pageNo <= doc.numPages; pageNo++) {
pageTextPromises.push(readPDFPage(doc, pageNo));
}
const pageTexts = await Promise.all(pageTextPromises);
resolve(pageTexts.join(''));
const content = await readPdfFile({ pdf: event.target.result });
resolve(content);
} catch (err) {
console.log(err, 'pdf load error');
reject('解析 PDF 失败');
@@ -122,7 +51,7 @@ export const readDocContent = (file: File, metadata: Record<string, any>) =>
});
const md = htmlStr2Md(html);
const rawText = await formatMarkdown(md, metadata);
const rawText = await uploadMarkdownBase64(md, metadata);
resolve(rawText);
} catch (error) {
@@ -156,7 +85,7 @@ export const readDocContent = (file: File, metadata: Record<string, any>) =>
*/
export const readCsvContent = async (file: File) => {
try {
const textArr = await readTxtContent(file);
const textArr = await readFileRawText(file);
const csvArr = Papa.parse(textArr).data as string[][];
if (csvArr.length === 0) {
throw new Error('csv 解析失败');
@@ -175,7 +104,7 @@ export const readCsvContent = async (file: File) => {
* 1. upload base64
* 2. replace \
*/
export const formatMarkdown = async (rawText: string = '', metadata: Record<string, any>) => {
export const uploadMarkdownBase64 = async (rawText: string = '', metadata: Record<string, any>) => {
// match base64, upload and replace it
const base64Regex = /data:image\/.*;base64,([^\)]+)/g;
const base64Arr = rawText.match(base64Regex) || [];

View File

@@ -94,7 +94,7 @@ export const useConfirm = (props?: {
<ModalFooter>
{showCancel && (
<Button
variant={'base'}
variant={'whiteBase'}
onClick={() => {
onClose();
typeof cancelCb.current === 'function' && cancelCb.current();

View File

@@ -96,7 +96,7 @@ export const useEditTitle = ({
/>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'base'} onClick={onClose}>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
</Button>
<Button onClick={onclickConfirm}></Button>

View File

@@ -60,9 +60,7 @@ export function usePagination<T = any>({
isDisabled={pageNum === 1}
icon={<ArrowBackIcon />}
aria-label={'left'}
size={'sm'}
w={'28px'}
h={'28px'}
size={'smSquare'}
isLoading={isLoading}
onClick={() => mutate(pageNum - 1)}
/>

View File

@@ -1,4 +1,4 @@
import { GET, POST, PUT, DELETE } from '@/web/common/api/request';
import type { InitDateResponse } from '@/global/common/api/systemRes';
export const getSystemInitData = () => GET<InitDateResponse>('/system/getInitData');
export const getSystemInitData = () => GET<InitDateResponse>('/common/system/getInitData');

View File

@@ -1,7 +1,7 @@
import type { InitDateResponse } from '@/global/common/api/systemRes';
import { getSystemInitData } from '@/web/common/system/api';
import { delay } from '@fastgpt/global/common/system/utils';
import type { FeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type';
import type {
ChatModelItemType,
@@ -12,7 +12,7 @@ import type {
AudioSpeechModelType
} from '@fastgpt/global/core/ai/model.d';
export let feConfigs: FeConfigsType = {};
export let feConfigs: FastGPTFeConfigsType = {};
export let priceMd = '';
export let systemVersion = '0.0.0';

View File

@@ -1,7 +1,11 @@
export enum EventNameEnum {
sendQuestion = 'sendQuestion',
editQuestion = 'editQuestion',
updaterNode = 'updaterNode'
// flow
requestFlowEvent = 'requestFlowEvent',
requestFlowStore = 'requestFlowStore',
receiveFlowStore = 'receiveFlowStore'
}
type EventNameType = `${EventNameEnum}`;

View File

@@ -168,7 +168,7 @@ const SelectCollections = ({
}}
{...(selected
? {
bg: 'blue.200'
bg: 'primary.200'
}
: {})}
onClick={() => {

View File

@@ -30,92 +30,158 @@ const Button = defineStyleConfig({
sizes: {
xs: {
fontSize: 'xs',
px: 3,
py: 0,
px: '8px',
py: '0',
h: '24px',
fontWeight: 'normal',
height: '22px',
borderRadius: '2px'
borderRadius: '8px'
},
sm: {
fontSize: 'sm',
px: 4,
py: 0,
fontWeight: 'normal',
height: '26px',
borderRadius: '2px'
},
md: {
fontSize: 'md',
px: 6,
py: 0,
height: '32px',
xsSquare: {
fontSize: 'xs',
px: '0',
py: '0',
h: '24px',
w: '24px',
fontWeight: 'normal',
borderRadius: '4px'
},
lg: {
fontSize: 'lg',
px: 8,
sm: {
fontSize: 'sm',
px: '14px',
py: 0,
height: '42px',
fontWeight: 'normal',
h: '30px',
borderRadius: '8px'
},
smSquare: {
fontSize: 'sm',
px: '0',
py: 0,
fontWeight: 'normal',
h: '30px',
w: '30px',
borderRadius: '8px'
},
md: {
fontSize: 'md',
px: '20px',
py: 0,
h: '36px',
fontWeight: 'normal',
borderRadius: '8px'
},
mdSquare: {
fontSize: 'md',
px: '0',
py: 0,
h: '36px',
w: '36px',
fontWeight: 'normal',
borderRadius: '6px'
},
lg: {
fontSize: 'md',
px: '20px',
py: 0,
h: '40px',
fontWeight: 'normal',
borderRadius: '8px'
},
lgSquare: {
fontSize: 'md',
px: '0',
py: 0,
h: '40px',
w: '40px',
fontWeight: 'normal',
borderRadius: '6px'
}
},
variants: {
primary: {
backgroundImage:
'linear-gradient(to bottom right, #2152d9 0%,#3370ff 40%, #4e83fd 100%) !important',
bg: 'primary.600',
color: 'white',
border: 'none',
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
filter: 'brightness(120%)'
},
_disabled: {
bg: '#3370ff !important'
bg: 'primary.7 !important'
}
},
gray: {
bg: '#F5F5F8',
color: 'blue.600',
border: '1px solid #EFF0F1',
_hover: {
background: '#3370FF1A'
}
},
base: {
color: 'myGray.900',
primaryOutline: {
color: 'primary.600',
border: '1px solid',
borderColor: 'myGray.200',
bg: 'transparent',
transition: 'background 0.3s',
borderColor: 'primary.300',
bg: 'white',
transition: 'background 0.1s',
boxShadow: '1',
_hover: {
color: 'blue.500',
background: 'myWhite.400',
boxShadow: '0 0 5px rgba(0,0,0,0.1)'
bg: 'primary.1'
},
_active: {
color: 'blue.600'
color: 'primary.600'
},
_disabled: {
bg: 'myGray.100 !important',
color: 'myGray.700 !important'
bg: 'white !important'
}
},
boxBtn: {
px: 3,
py: '2px',
borderRadius: 'md',
whiteBase: {
color: 'myGray.600',
border: '1px solid',
borderColor: 'myGray.250',
bg: 'white',
transition: 'background 0.1s',
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
bg: 'myGray.200'
color: 'primary.600'
},
_active: {
color: 'primary.600'
}
},
blue: {
borderRadius: 'md',
bg: '#3370FF',
color: 'white',
fontSize: 'sm',
whitePrimary: {
color: 'myGray.600',
border: '1px solid',
borderColor: 'myGray.250',
bg: 'white',
transition: 'background 0.1s',
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
bg: '#145BFF'
color: 'primary.600',
background: 'primary.1',
borderColor: 'primary.300'
},
_active: {
color: 'primary.600'
}
},
whiteDanger: {
color: 'myGray.600',
border: '1px solid',
borderColor: 'myGray.250',
bg: 'white',
transition: 'background 0.1s',
boxShadow: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
_hover: {
color: 'red.600',
background: 'red.1',
borderColor: 'red.300'
},
_active: {
color: 'red.600'
}
},
grayBase: {
bg: 'myGray.150',
color: 'myGray.900',
_hover: {
color: 'primary.600',
bg: 'primary.50'
},
_disabled: {
bg: 'myGray.50'
}
}
},
@@ -126,17 +192,19 @@ const Button = defineStyleConfig({
});
const Input: ComponentStyleConfig = {
baseStyle: {},
baseStyle: {
fontsize: '14px'
},
variants: {
outline: {
field: {
backgroundColor: 'transparent',
h: '40px',
border: '1px solid',
borderRadius: 'base',
borderColor: 'myGray.200',
borderRadius: 'md',
borderColor: 'borderColor.low',
_focus: {
borderColor: 'blue.500',
boxShadow: '0px 0px 4px #A8DBFF',
borderColor: 'primary.500',
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)',
bg: 'white'
},
_disabled: {
@@ -161,8 +229,8 @@ const NumberInput = numInputMultiStyle({
borderRadius: 'base',
borderColor: 'myGray.200',
_focus: {
borderColor: 'blue.500 !important',
boxShadow: '0px 0px 4px #A8DBFF !important',
borderColor: 'primary.500 !important',
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15) !important',
bg: 'transparent'
},
_disabled: {
@@ -175,7 +243,7 @@ const NumberInput = numInputMultiStyle({
border: 'none',
color: 'myGray.600',
_active: {
color: 'blue.500'
color: 'primary.500'
}
}
})
@@ -195,8 +263,8 @@ const Textarea: ComponentStyleConfig = {
borderColor: ''
},
_focus: {
borderColor: 'blue.500',
boxShadow: '0px 0px 4px #A8DBFF',
borderColor: 'primary.500',
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)',
bg: 'white'
}
}
@@ -213,7 +281,7 @@ const Switch = switchMultiStyle({
track: {
bg: 'myGray.100',
_checked: {
bg: 'blue.600'
bg: 'primary.600'
}
}
})
@@ -225,8 +293,8 @@ const Select = selectMultiStyle({
field: {
borderColor: 'myGray.200',
_focusWithin: {
boxShadow: '0px 0px 4px #A8DBFF',
borderColor: 'blue.500'
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)',
borderColor: 'primary.500'
}
}
})
@@ -254,7 +322,7 @@ export const theme = extendTheme({
// lineHeight: 'unset'
},
a: {
color: 'blue.600'
color: 'primary.600'
}
}
},
@@ -272,18 +340,27 @@ export const theme = extendTheme({
1000: '#313132'
},
myGray: {
100: '#EFF0F1',
200: '#DEE0E2',
300: '#BDC1C5',
400: '#9CA2A8',
500: '#7B838B',
600: '#5A646E',
700: '#485058',
800: '#363C42',
900: '#24282C',
1000: '#121416'
25: '#FBFBFC',
50: '#F7F8FA',
100: '#F4F4F7',
150: '#F0F1F6',
200: '#E8EBF0',
250: '#DFE2EA',
300: '#C4CBD7',
400: '#8A95A7',
500: '#667085',
600: '#485264',
700: '#383F50',
800: '#1D2532',
900: '#111824'
},
blue: {
primary: {
1: 'rgba(51, 112, 255, 0.1)',
3: 'rgba(51, 112, 255, 0.3)',
5: 'rgba(51, 112, 255, 0.5)',
7: 'rgba(51, 112, 255, 0.7)',
9: 'rgba(51, 112, 255, 0.9)',
50: '#F0F4FF',
100: '#E1EAFF',
200: '#C5D7FF',
@@ -295,33 +372,87 @@ export const theme = extendTheme({
800: '#2450B5',
900: '#1D4091'
},
myRead: {
600: '#ff4d4f'
red: {
1: 'rgba(217,45,32,0.1)',
3: 'rgba(217,45,32,0.3)',
5: 'rgba(217,45,32,0.5)',
25: '#FFFBFA',
50: '#FEF3F2',
100: '#FEE4E2',
200: '#FECDCA',
300: '#FDA29B',
400: '#F97066',
500: '#F04438',
600: '#D92D20',
700: '#B42318',
800: '#912018',
900: '#7A271A'
},
green: {
25: '#F9FEFB',
50: '#EDFBF3',
100: '#D1FADF',
200: '#B9F4D1',
300: '#76E4AA',
400: '#32D583',
500: '#12B76A',
600: '#039855',
700: '#027A48',
800: '#05603A',
900: '#054F31'
},
borderColor: {
low: '#E8EBF0',
base: '#DFE2EA',
high: '#C4CBD7',
highest: '#8A95A7'
}
},
fonts: {
body: '-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"'
body: 'PingFang,Noto Sans,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"'
},
fontSizes: {
xs: '10px',
xs: '10',
sm: '12px',
md: '14px',
lg: '16px',
xl: '16px',
'2xl': '18px',
'3xl': '20px'
xl: '18px',
'2xl': '20px',
'3xl': '24px',
'4xl': '28px',
'5xl': '32px',
'6xl': '36px'
},
borderColor: {
low: 'myGray.200',
common: 'myGray.250',
high: 'myGray.300',
highest: 'myGray.400'
},
borders: {
sm: '1px solid #EFF0F1',
base: '1px solid #DEE0E2',
sm: '1px solid #E8EBF0',
base: '1px solid #DFE2EA',
md: '1px solid #DAE0E2',
lg: '1px solid #D0E0E2'
},
borderRadius: {
xs: '4px',
sm: '6px',
md: '8px',
lg: '12px',
xl: '16px'
},
shadows: {
sm: '0 0 5px rgba(0,0,0,0.1)',
md: '0 0 8px rgba(0,0,0,0.1)',
base: '0 0 10px rgba(0,0,0,0.15)',
lg: '0 0 10px rgba(0,0,0,0.2)'
1: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 1px 2px 0px rgba(19, 51, 107, 0.05)',
1.5: '0px 0px 1px 0px rgba(19, 51, 107, 0.15), 0px 1px 2px 0px rgba(19, 51, 107, 0.10)',
2: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 4px 4px 0px rgba(19, 51, 107, 0.05)',
3: '0px 0px 1px 0px rgba(19, 51, 107, 0.08), 0px 4px 10px 0px rgba(19, 51, 107, 0.08)',
3.5: '0px 0px 1px 0px rgba(19, 51, 107, 0.10), 0px 4px 10px 0px rgba(19, 51, 107, 0.10)',
4: '0px 0px 1px 0px rgba(19, 51, 107, 0.20), 0px 12px 16px -4px rgba(19, 51, 107, 0.20)',
5: '0px 0px 1px 0px rgba(19, 51, 107, 0.15), 0px 20px 24px -8px rgba(19, 51, 107, 0.15)',
6: '0px 0px 1px 0px rgba(19, 51, 107, 0.20), 0px 24px 48px -12px rgba(19, 51, 107, 0.20)',
7: '0px 0px 1px 0px rgba(19, 51, 107, 0.20), 0px 32px 64px -12px rgba(19, 51, 107, 0.20)'
},
breakpoints: {
sm: '900px',

View File

@@ -17,7 +17,7 @@ export const sendAuthCode = (data: {
}) => POST(`/plusApi/support/user/inform/sendAuthCode`, data);
export const getTokenLogin = () =>
GET<UserType>('/user/account/tokenLogin', {}, { maxQuantity: 1 });
GET<UserType>('/support/user/account/tokenLogin', {}, { maxQuantity: 1 });
export const oauthLogin = (params: OauthLoginProps) =>
POST<ResLogin>('/plusApi/support/user/account/login/oauth', params);
export const postFastLogin = (params: FastLoginProps) =>
@@ -57,17 +57,17 @@ export const postFindPassword = ({
});
export const updatePasswordByOld = ({ oldPsw, newPsw }: { oldPsw: string; newPsw: string }) =>
POST('/user/account/updatePasswordByOld', {
POST('/support/user/account/updatePasswordByOld', {
oldPsw: hashStr(oldPsw),
newPsw: hashStr(newPsw)
});
export const postLogin = ({ password, ...props }: PostLoginProps) =>
POST<ResLogin>('/user/account/loginByPassword', {
POST<ResLogin>('/support/user/account/loginByPassword', {
...props,
password: hashStr(password)
});
export const loginOut = () => GET('/user/account/loginout');
export const loginOut = () => GET('/support/user/account/loginout');
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/account/update', data);
export const putUserInfo = (data: UserUpdateParams) => PUT('/support/user/account/update', data);

View File

@@ -4,14 +4,17 @@ import { UserAuthTypeEnum } from '@fastgpt/global/support/user/constant';
import { useToast } from '@/web/common/hooks/useToast';
import { feConfigs } from '@/web/common/system/staticData';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'next-i18next';
let timer: any;
export const useSendCode = () => {
const { t } = useTranslation();
const { toast } = useToast();
const [codeSending, setCodeSending] = useState(false);
const [codeCountDown, setCodeCountDown] = useState(0);
const sendCodeText = useMemo(() => {
if (codeSending) return t('support.user.auth.Sending Code');
if (codeCountDown >= 10) {
return `${codeCountDown}s后重新获取`;
}
@@ -19,10 +22,11 @@ export const useSendCode = () => {
return `0${codeCountDown}s后重新获取`;
}
return '获取验证码';
}, [codeCountDown]);
}, [codeCountDown, codeSending, t]);
const sendCode = useCallback(
async ({ username, type }: { username: string; type: `${UserAuthTypeEnum}` }) => {
if (codeCountDown > 0) return;
setCodeSending(true);
try {
await sendAuthCode({
@@ -52,7 +56,7 @@ export const useSendCode = () => {
}
setCodeSending(false);
},
[toast]
[codeCountDown, toast]
);
return {