Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17364e9da3 | ||
|
|
2390823282 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -34,6 +34,6 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
public/trainData/
|
/public/trainData/
|
||||||
.vscode/
|
/.vscode/
|
||||||
platform.json
|
platform.json
|
||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"editor.formatOnType": true,
|
|
||||||
"editor.formatOnSave": true ,
|
|
||||||
"prettier.tabWidth": 2
|
|
||||||
}
|
|
||||||
62
README.md
62
README.md
@@ -15,22 +15,62 @@ TOKEN_KEY=随便填一个,用于生成和校验token
|
|||||||
```bash
|
```bash
|
||||||
pnpm dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
## 部署
|
## 部署
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 本地 docker 打包
|
# 本地 docker 打包
|
||||||
docker build -t imageName .
|
docker build -t imageName:tag .
|
||||||
docker push imageName
|
docker push imageName:tag
|
||||||
|
```
|
||||||
# 服务器拉取部署
|
|
||||||
docker pull imageName
|
服务器请准备好 docker, mongo,nginx和代理。 镜像走本机的代理,所以用 host,port改成代理的端口,clash一般都是7890。
|
||||||
docker stop doc-gpt || true
|
|
||||||
docker rm doc-gpt || true
|
```bash
|
||||||
# 运行时才把参数写入
|
# 服务器拉取部署, imageName 替换成镜像名
|
||||||
docker run -d --network=host --name doc-gpt -e AXIOS_PROXY_HOST= -e AXIOS_PROXY_PORT= -e MAILE_CODE= -e TOKEN_KEY= -e MONGODB_URI= imageName
|
docker pull imageName:tag
|
||||||
|
# 获取本地旧镜像ID
|
||||||
|
OLD_IMAGE_ID=$(docker images imageName -f "dangling=true" -q)
|
||||||
|
docker stop doc-gpt || true
|
||||||
|
docker rm doc-gpt || true
|
||||||
|
docker run -d --network=host --name doc-gpt \
|
||||||
|
-e MAX_USER=50 \
|
||||||
|
-e AXIOS_PROXY_HOST=127.0.0.1 \
|
||||||
|
-e AXIOS_PROXY_PORT=7890 \
|
||||||
|
-e MY_MAIL=your email\
|
||||||
|
-e MAILE_CODE=your email code \
|
||||||
|
-e TOKEN_KEY=任意一个内容 \
|
||||||
|
-e MONGODB_URI="mongodb://aha:ROOT_root123@127.0.0.0:27017/?authSource=admin&readPreference=primary&appname=MongoDB%20Compass&ssl=false" \
|
||||||
|
imageName:tag
|
||||||
|
docker logs doc-gpt
|
||||||
|
|
||||||
|
|
||||||
|
# 删除本地旧镜像
|
||||||
|
if [ ! -z "$OLD_IMAGE_ID" ]; then
|
||||||
|
docker rmi $OLD_IMAGE_ID
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker 安装
|
||||||
|
```bash
|
||||||
|
# 安装docker
|
||||||
|
curl -sSL https://get.daocloud.io/docker | sh
|
||||||
|
sudo systemctl start docker
|
||||||
|
```
|
||||||
|
|
||||||
|
### mongo 安装
|
||||||
|
```bash
|
||||||
|
docker pull mongo:6.0.4
|
||||||
|
docker stop mongo
|
||||||
|
docker rm mongo
|
||||||
|
docker run -d --name mongo \
|
||||||
|
-e MONGO_INITDB_ROOT_USERNAME= \
|
||||||
|
-e MONGO_INITDB_ROOT_PASSWORD= \
|
||||||
|
-v /root/service/mongo:/data/db \
|
||||||
|
mongo:6.0.4
|
||||||
```
|
```
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
||||||
|
|
||||||
# 介绍页
|
# 介绍页
|
||||||
|
|
||||||
@@ -70,4 +110,4 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
|
|||||||
|
|
||||||
### 其他问题
|
### 其他问题
|
||||||
还有其他问题,可以加我 wx,拉个交流群大家一起聊聊。
|
还有其他问题,可以加我 wx,拉个交流群大家一起聊聊。
|
||||||

|

|
||||||
@@ -6,7 +6,17 @@ const isDev = process.env.NODE_ENV === 'development';
|
|||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
output: 'standalone',
|
output: 'standalone',
|
||||||
reactStrictMode: false,
|
reactStrictMode: false,
|
||||||
compress: true
|
compress: true,
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'docgpt-1301319986.cos.ap-shanghai.myqcloud.com',
|
||||||
|
port: '',
|
||||||
|
pathname: '/**'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = nextConfig;
|
module.exports = nextConfig;
|
||||||
|
|||||||
@@ -23,8 +23,6 @@
|
|||||||
"axios": "^1.3.3",
|
"axios": "^1.3.3",
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"eslint": "8.34.0",
|
|
||||||
"eslint-config-next": "13.1.6",
|
|
||||||
"formidable": "^2.1.1",
|
"formidable": "^2.1.1",
|
||||||
"framer-motion": "^9.0.6",
|
"framer-motion": "^9.0.6",
|
||||||
"hyperdown": "^2.4.29",
|
"hyperdown": "^2.4.29",
|
||||||
@@ -40,7 +38,9 @@
|
|||||||
"react-hook-form": "^7.43.1",
|
"react-hook-form": "^7.43.1",
|
||||||
"react-markdown": "^8.0.5",
|
"react-markdown": "^8.0.5",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
|
"rehype-katex": "^6.0.2",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
|
"remark-math": "^5.1.1",
|
||||||
"sass": "^1.58.3",
|
"sass": "^1.58.3",
|
||||||
"sharp": "^0.31.3",
|
"sharp": "^0.31.3",
|
||||||
"tunnel": "^0.0.6",
|
"tunnel": "^0.0.6",
|
||||||
@@ -58,6 +58,8 @@
|
|||||||
"@types/react-syntax-highlighter": "^15.5.6",
|
"@types/react-syntax-highlighter": "^15.5.6",
|
||||||
"@types/tunnel": "^0.0.3",
|
"@types/tunnel": "^0.0.3",
|
||||||
"@types/uuid": "^9.0.1",
|
"@types/uuid": "^9.0.1",
|
||||||
|
"eslint": "8.34.0",
|
||||||
|
"eslint-config-next": "13.1.6",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^13.1.2",
|
"lint-staged": "^13.1.2",
|
||||||
"prettier": "^2.8.4"
|
"prettier": "^2.8.4"
|
||||||
|
|||||||
612
pnpm-lock.yaml
generated
612
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
BIN
public/icon/logo.png
Normal file
BIN
public/icon/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 117 KiB |
BIN
public/logo.png
BIN
public/logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 117 KiB |
@@ -1,5 +1,6 @@
|
|||||||
import { GET, POST, DELETE } from './request';
|
import { GET, POST, DELETE } from './request';
|
||||||
import { ChatItemType, ChatSiteType, ChatSiteItemType } from '@/types/chat';
|
import { ChatItemType, ChatSiteType, ChatSiteItemType } from '@/types/chat';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取一个聊天框的ID
|
* 获取一个聊天框的ID
|
||||||
@@ -56,7 +57,7 @@ export const postChatGptPrompt = ({
|
|||||||
});
|
});
|
||||||
/* 获取 Chat 的 Event 对象,进行持续通信 */
|
/* 获取 Chat 的 Event 对象,进行持续通信 */
|
||||||
export const getChatGPTSendEvent = (chatId: string, windowId: string) =>
|
export const getChatGPTSendEvent = (chatId: string, windowId: string) =>
|
||||||
new EventSource(`/api/chat/chatGpt?chatId=${chatId}&windowId=${windowId}`);
|
new EventSource(`/api/chat/chatGpt?chatId=${chatId}&windowId=${windowId}&date=${Date.now()}`);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除最后一句
|
* 删除最后一句
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ function responseSuccess(response: AxiosResponse<ResponseDataType>) {
|
|||||||
*/
|
*/
|
||||||
function checkRes(data: ResponseDataType) {
|
function checkRes(data: ResponseDataType) {
|
||||||
if (data === undefined) {
|
if (data === undefined) {
|
||||||
console.log(data, 'data is empty');
|
console.error(data, 'data is empty');
|
||||||
return Promise.reject('服务器异常');
|
return Promise.reject('服务器异常');
|
||||||
} else if (data.code < 200 || data.code >= 400) {
|
} else if (data.code < 200 || data.code >= 400) {
|
||||||
return Promise.reject(data.message);
|
return Promise.reject(data.message);
|
||||||
@@ -49,21 +49,20 @@ function responseError(err: any) {
|
|||||||
console.error('请求错误', err);
|
console.error('请求错误', err);
|
||||||
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
return Promise.reject('未知错误');
|
return Promise.reject({ message: '未知错误' });
|
||||||
}
|
}
|
||||||
if (typeof err === 'string') {
|
if (typeof err === 'string') {
|
||||||
return Promise.reject(err);
|
return Promise.reject({ message: err });
|
||||||
}
|
}
|
||||||
if (err.response) {
|
if (err.response) {
|
||||||
// 有报错响应
|
// 有报错响应
|
||||||
const res = err.response;
|
const res = err.response;
|
||||||
/* token过期,判断请求token与本地是否相同,若不同需要重发 */
|
|
||||||
if (res.data.code in TOKEN_ERROR_CODE) {
|
if (res.data.code in TOKEN_ERROR_CODE) {
|
||||||
clearToken();
|
clearToken();
|
||||||
return Promise.reject('token过期,重新登录');
|
return Promise.reject({ message: 'token过期,重新登录' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.reject('未知错误');
|
return Promise.reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 创建请求实例 */
|
/* 创建请求实例 */
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const Auth = ({ children }: { children: JSX.Element }) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError(error) {
|
onError(error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
router.push('/login');
|
router.push('/login');
|
||||||
toast();
|
toast();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const Navbar = ({
|
|||||||
>
|
>
|
||||||
{/* logo */}
|
{/* logo */}
|
||||||
<Box pb={4}>
|
<Box pb={4}>
|
||||||
<Image src={'/logo.png'} width={'35'} height={'35'} alt=""></Image>
|
<Image src={'/icon/logo.png'} width={'35'} height={'35'} alt=""></Image>
|
||||||
</Box>
|
</Box>
|
||||||
{/* 导航列表 */}
|
{/* 导航列表 */}
|
||||||
<Box flex={1}>
|
<Box flex={1}>
|
||||||
@@ -46,6 +46,7 @@ const Navbar = ({
|
|||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
justifyContent={'center'}
|
justifyContent={'center'}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
!item.activeLink.includes(router.pathname) &&
|
||||||
router.push(item.link, undefined, {
|
router.push(item.link, undefined, {
|
||||||
shallow: true
|
shallow: true
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const NavbarPhone = ({
|
|||||||
<DrawerContent maxWidth={'50vw'}>
|
<DrawerContent maxWidth={'50vw'}>
|
||||||
<DrawerBody p={4}>
|
<DrawerBody p={4}>
|
||||||
<Box py={4}>
|
<Box py={4}>
|
||||||
<Image src={'/logo.png'} margin={'auto'} w={'35'} h={'35'} alt=""></Image>
|
<Image src={'/icon/logo.png'} margin={'auto'} w={'35'} h={'35'} alt=""></Image>
|
||||||
</Box>
|
</Box>
|
||||||
{navbarList.map((item) => (
|
{navbarList.map((item) => (
|
||||||
<Flex
|
<Flex
|
||||||
|
|||||||
@@ -1,5 +1,47 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
export const codeLight: { [key: string]: React.CSSProperties } = {
|
export const codeLight: { [key: string]: React.CSSProperties } = {
|
||||||
|
'code[class*=language-]': {
|
||||||
|
color: '#d4d4d4',
|
||||||
|
fontSize: '13px',
|
||||||
|
textShadow: 'none',
|
||||||
|
fontFamily: 'Menlo,Monaco,Consolas,"Andale Mono","Ubuntu Mono","Courier New",monospace',
|
||||||
|
direction: 'ltr',
|
||||||
|
textAlign: 'left',
|
||||||
|
whiteSpace: 'pre',
|
||||||
|
wordSpacing: 'normal',
|
||||||
|
wordBreak: 'normal',
|
||||||
|
lineHeight: '1.5',
|
||||||
|
MozTabSize: '4',
|
||||||
|
OTabSize: '4',
|
||||||
|
tabSize: '4',
|
||||||
|
WebkitHyphens: 'none',
|
||||||
|
MozHyphens: 'none',
|
||||||
|
msHyphens: 'none',
|
||||||
|
hyphens: 'none'
|
||||||
|
},
|
||||||
|
'pre[class*=language-]': {
|
||||||
|
color: '#d4d4d4',
|
||||||
|
fontSize: '13px',
|
||||||
|
textShadow: 'none',
|
||||||
|
fontFamily: 'Menlo,Monaco,Consolas,"Andale Mono","Ubuntu Mono","Courier New",monospace',
|
||||||
|
direction: 'ltr',
|
||||||
|
textAlign: 'left',
|
||||||
|
whiteSpace: 'pre',
|
||||||
|
wordSpacing: 'normal',
|
||||||
|
wordBreak: 'normal',
|
||||||
|
lineHeight: '1.5',
|
||||||
|
MozTabSize: '4',
|
||||||
|
OTabSize: '4',
|
||||||
|
tabSize: '4',
|
||||||
|
WebkitHyphens: 'none',
|
||||||
|
MozHyphens: 'none',
|
||||||
|
msHyphens: 'none',
|
||||||
|
hyphens: 'none',
|
||||||
|
padding: '1em',
|
||||||
|
margin: '.5em 0',
|
||||||
|
overflow: 'auto',
|
||||||
|
background: '#1e1e1e'
|
||||||
|
},
|
||||||
'code[class*=language-] ::selection': {
|
'code[class*=language-] ::selection': {
|
||||||
textShadow: 'none',
|
textShadow: 'none',
|
||||||
background: '#264f78'
|
background: '#264f78'
|
||||||
|
|||||||
@@ -107,7 +107,6 @@
|
|||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
.markdown h2 {
|
.markdown h2 {
|
||||||
border-bottom: 1px solid #cccccc;
|
|
||||||
color: #000000;
|
color: #000000;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
@@ -329,7 +328,6 @@
|
|||||||
border-radius: 3px 3px 3px 3px;
|
border-radius: 3px 3px 3px 3px;
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
.markdown pre > code {
|
.markdown pre > code {
|
||||||
background: none repeat scroll 0 0 transparent;
|
background: none repeat scroll 0 0 transparent;
|
||||||
@@ -376,4 +374,9 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: 'Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji';
|
font-family: 'Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: var(--chakra-colors-blue-600);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
import React, { memo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import remarkGfm from 'remark-gfm';
|
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||||
import { codeLight } from './codeLight';
|
import { codeLight } from './codeLight';
|
||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import { useCopyData } from '@/utils/tools';
|
import { useCopyData } from '@/utils/tools';
|
||||||
import Icon from '@/components/Icon';
|
import Icon from '@/components/Icon';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
import remarkMath from 'remark-math';
|
||||||
|
import rehypeKatex from 'rehype-katex';
|
||||||
|
|
||||||
const Markdown = ({ source, isChatting }: { source: string; isChatting: boolean }) => {
|
const Markdown = ({ source, isChatting }: { source: string; isChatting: boolean }) => {
|
||||||
// const formatSource = useMemo(() => source.replace(/\n/g, '\n'), [source]);
|
const formatSource = useMemo(() => source.replace(/\n/g, ' \n'), [source]);
|
||||||
const { copyData } = useCopyData();
|
const { copyData } = useCopyData();
|
||||||
// console.log(source);
|
|
||||||
return (
|
return (
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
className={`${styles.markdown} ${
|
className={`${styles.markdown} ${
|
||||||
isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''
|
isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''
|
||||||
}`}
|
}`}
|
||||||
rehypePlugins={[remarkGfm]}
|
remarkPlugins={[remarkMath]}
|
||||||
|
rehypePlugins={[remarkGfm, rehypeKatex]}
|
||||||
components={{
|
components={{
|
||||||
pre: 'div',
|
pre: 'div',
|
||||||
code({ node, inline, className, children, ...props }) {
|
code({ node, inline, className, children, ...props }) {
|
||||||
@@ -55,8 +58,9 @@ const Markdown = ({ source, isChatting }: { source: string; isChatting: boolean
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
linkTarget="_blank"
|
||||||
>
|
>
|
||||||
{source}
|
{formatSource}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,6 +38,5 @@ export const introPage = `
|
|||||||
* 分享链接应为:http://docgpt.ahapocket.cn/chat?chatId=6402c9f64cb5d6283f764
|
* 分享链接应为:http://docgpt.ahapocket.cn/chat?chatId=6402c9f64cb5d6283f764
|
||||||
|
|
||||||
### 其他问题
|
### 其他问题
|
||||||
还有其他问题,可以加我 wx,拉个交流群大家一起聊聊。
|
还有其他问题,可以加我 wx: YNyiqi,拉个交流群大家一起聊聊。
|
||||||

|
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useRef } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogBody,
|
AlertDialogBody,
|
||||||
@@ -17,45 +17,51 @@ export const useConfirm = ({ title = '提示', content }: { title?: string; cont
|
|||||||
const cancelCb = useRef<any>();
|
const cancelCb = useRef<any>();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
openConfirm: (confirm?: any, cancel?: any) => {
|
openConfirm: useCallback(
|
||||||
onOpen();
|
(confirm?: any, cancel?: any) => {
|
||||||
confirmCb.current = confirm;
|
onOpen();
|
||||||
cancelCb.current = cancel;
|
confirmCb.current = confirm;
|
||||||
},
|
cancelCb.current = cancel;
|
||||||
ConfirmChild: () => (
|
},
|
||||||
<AlertDialog isOpen={isOpen} leastDestructiveRef={cancelRef} onClose={onClose}>
|
[onOpen]
|
||||||
<AlertDialogOverlay>
|
),
|
||||||
<AlertDialogContent>
|
ConfirmChild: useCallback(
|
||||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
() => (
|
||||||
{title}
|
<AlertDialog isOpen={isOpen} leastDestructiveRef={cancelRef} onClose={onClose}>
|
||||||
</AlertDialogHeader>
|
<AlertDialogOverlay>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||||
|
{title}
|
||||||
|
</AlertDialogHeader>
|
||||||
|
|
||||||
<AlertDialogBody>{content}</AlertDialogBody>
|
<AlertDialogBody>{content}</AlertDialogBody>
|
||||||
|
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<Button
|
<Button
|
||||||
colorScheme={'gray'}
|
colorScheme={'gray'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onClose();
|
onClose();
|
||||||
typeof cancelCb.current === 'function' && cancelCb.current();
|
typeof cancelCb.current === 'function' && cancelCb.current();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
取消
|
取消
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
colorScheme="blue"
|
colorScheme="blue"
|
||||||
ml={3}
|
ml={4}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onClose();
|
onClose();
|
||||||
typeof confirmCb.current === 'function' && confirmCb.current();
|
typeof confirmCb.current === 'function' && confirmCb.current();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
确认
|
确认
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialogOverlay>
|
</AlertDialogOverlay>
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
|
),
|
||||||
|
[content, isOpen, onClose, title]
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,36 +1,33 @@
|
|||||||
import { useState, memo } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { Spinner, Flex } from '@chakra-ui/react';
|
import { Spinner, Flex } from '@chakra-ui/react';
|
||||||
|
|
||||||
export const useLoading = (props?: { defaultLoading: boolean }) => {
|
export const useLoading = (props?: { defaultLoading: boolean }) => {
|
||||||
const [isLoading, setIsLoading] = useState(props?.defaultLoading || false);
|
const [isLoading, setIsLoading] = useState(props?.defaultLoading || false);
|
||||||
|
|
||||||
const Loading = ({
|
const Loading = useCallback(
|
||||||
loading,
|
({ loading, fixed = true }: { loading?: boolean; fixed?: boolean }): JSX.Element | null => {
|
||||||
fixed = true
|
return isLoading || loading ? (
|
||||||
}: {
|
<Flex
|
||||||
loading?: boolean;
|
position={fixed ? 'fixed' : 'absolute'}
|
||||||
fixed?: boolean;
|
zIndex={100}
|
||||||
}): JSX.Element | null => {
|
backgroundColor={'rgba(255,255,255,0.5)'}
|
||||||
return isLoading || loading ? (
|
top={0}
|
||||||
<Flex
|
left={0}
|
||||||
position={fixed ? 'fixed' : 'absolute'}
|
right={0}
|
||||||
zIndex={100}
|
bottom={0}
|
||||||
backgroundColor={'rgba(255,255,255,0.5)'}
|
alignItems={'center'}
|
||||||
top={0}
|
justifyContent={'center'}
|
||||||
left={0}
|
>
|
||||||
right={0}
|
<Spinner thickness="4px" speed="0.65s" emptyColor="gray.200" color="blue.500" size="xl" />
|
||||||
bottom={0}
|
</Flex>
|
||||||
alignItems={'center'}
|
) : null;
|
||||||
justifyContent={'center'}
|
},
|
||||||
>
|
[isLoading]
|
||||||
<Spinner thickness="4px" speed="0.65s" emptyColor="gray.200" color="blue.500" size="xl" />
|
);
|
||||||
</Flex>
|
|
||||||
) : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoading,
|
isLoading,
|
||||||
setIsLoading,
|
setIsLoading,
|
||||||
Loading: memo(Loading)
|
Loading
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,18 +15,18 @@ Router.events.on('routeChangeStart', () => NProgress.start());
|
|||||||
Router.events.on('routeChangeComplete', () => NProgress.done());
|
Router.events.on('routeChangeComplete', () => NProgress.done());
|
||||||
Router.events.on('routeChangeError', () => NProgress.done());
|
Router.events.on('routeChangeError', () => NProgress.done());
|
||||||
|
|
||||||
export default function App({ Component, pageProps }: AppProps) {
|
// Create a client
|
||||||
// Create a client
|
const queryClient = new QueryClient({
|
||||||
const queryClient = new QueryClient({
|
defaultOptions: {
|
||||||
defaultOptions: {
|
queries: {
|
||||||
queries: {
|
refetchOnWindowFocus: false,
|
||||||
refetchOnWindowFocus: false,
|
retry: false,
|
||||||
retry: false,
|
cacheTime: 0
|
||||||
cacheTime: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function App({ Component, pageProps }: AppProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { connectToDatabase, Chat, ChatWindow } from '@/service/mongo';
|
import { Readable } from 'stream';
|
||||||
|
import { connectToDatabase, ChatWindow } from '@/service/mongo';
|
||||||
import type { ModelType } from '@/types/model';
|
import type { ModelType } from '@/types/model';
|
||||||
import { getOpenAIApi, authChat } from '@/service/utils/chat';
|
import { getOpenAIApi, authChat } from '@/service/utils/chat';
|
||||||
import { openaiProxy } from '@/service/utils/tools';
|
import { openaiProxy } from '@/service/utils/tools';
|
||||||
@@ -9,12 +9,23 @@ import { ChatItemType } from '@/types/chat';
|
|||||||
|
|
||||||
/* 发送提示词 */
|
/* 发送提示词 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
res.writeHead(200, {
|
res.setHeader('Connection', 'keep-alive');
|
||||||
Connection: 'keep-alive',
|
res.setHeader('Cache-Control', 'no-cache');
|
||||||
'Content-Encoding': 'none',
|
res.setHeader('Content-Type', 'text/event-stream');
|
||||||
'Cache-Control': 'no-cache',
|
|
||||||
'Content-Type': 'text/event-stream'
|
const responseData: string[] = [];
|
||||||
|
const stream = new Readable({
|
||||||
|
read(size) {
|
||||||
|
const data = responseData.shift() || null;
|
||||||
|
this.push(data);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
res.on('close', () => {
|
||||||
|
res.end();
|
||||||
|
stream.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
const { chatId, windowId } = req.query as { chatId: string; windowId: string };
|
const { chatId, windowId } = req.query as { chatId: string; windowId: string };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -47,14 +58,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const formatPrompts: ChatCompletionRequestMessage[] = filterPrompts.map(
|
const formatPrompts: ChatCompletionRequestMessage[] = filterPrompts.map(
|
||||||
(item: ChatItemType) => ({
|
(item: ChatItemType) => ({
|
||||||
role: map[item.obj],
|
role: map[item.obj],
|
||||||
content: item.value
|
content: item.value.replace(/(\n| )/g, '')
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
// 第一句话,强调代码类型
|
// 第一句话,强调代码类型
|
||||||
formatPrompts.unshift({
|
formatPrompts.unshift({
|
||||||
role: ChatCompletionRequestMessageRoleEnum.System,
|
role: ChatCompletionRequestMessageRoleEnum.System,
|
||||||
content:
|
content:
|
||||||
'If the content is code or code blocks, please label the code type as accurately as possible.'
|
'If the content is code or code blocks, please mark the code type as accurately as possible!'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取 chatAPI
|
// 获取 chatAPI
|
||||||
@@ -74,43 +85,75 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
const reg = /{"content"(.*)"}/g;
|
const reg = /{"content"(.*)"}/g;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const match = chatResponse.data.match(reg);
|
const match = chatResponse.data.match(reg);
|
||||||
|
if (!match) return;
|
||||||
|
|
||||||
let AIResponse = '';
|
let AIResponse = '';
|
||||||
if (match) {
|
|
||||||
match.forEach((item: string, i: number) => {
|
|
||||||
try {
|
|
||||||
const json = JSON.parse(item);
|
|
||||||
// 开头的换行忽略
|
|
||||||
if (i === 0 && json.content?.startsWith('\n')) return;
|
|
||||||
AIResponse += json.content;
|
|
||||||
const content = json.content.replace(/\n/g, '<br/>'); // 无法直接传输\n
|
|
||||||
content && res.write(`data: ${content}\n\n`);
|
|
||||||
} catch (err) {
|
|
||||||
err;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
res.write(`data: [DONE]\n\n`);
|
|
||||||
|
|
||||||
|
// 循环给 stream push 内容
|
||||||
|
match.forEach((item: string, i: number) => {
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(item);
|
||||||
|
// 开头的换行忽略
|
||||||
|
if (i === 0 && json.content?.startsWith('\n')) return;
|
||||||
|
AIResponse += json.content;
|
||||||
|
const content = json.content.replace(/\n/g, '<br/>'); // 无法直接传输\n
|
||||||
|
if (content) {
|
||||||
|
responseData.push(`event: responseData\ndata: ${content}\n\n`);
|
||||||
|
// res.write(`event: responseData\n`)
|
||||||
|
// res.write(`data: ${content}\n\n`)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
responseData.push(`event: done\ndata: \n\n`);
|
||||||
// 存入库
|
// 存入库
|
||||||
await ChatWindow.findByIdAndUpdate(windowId, {
|
(async () => {
|
||||||
$push: {
|
await ChatWindow.findByIdAndUpdate(windowId, {
|
||||||
content: {
|
$push: {
|
||||||
obj: 'AI',
|
content: {
|
||||||
value: AIResponse
|
obj: 'AI',
|
||||||
}
|
value: AIResponse
|
||||||
},
|
}
|
||||||
updateTime: Date.now()
|
},
|
||||||
});
|
updateTime: Date.now()
|
||||||
|
});
|
||||||
res.end();
|
})();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.log(err?.response?.data || err);
|
let errorText = err;
|
||||||
// 删除最一条数据库记录, 也就是预发送的那一条
|
if (err.code === 'ECONNRESET') {
|
||||||
await ChatWindow.findByIdAndUpdate(windowId, {
|
errorText = '服务器代理出错';
|
||||||
$pop: { content: 1 },
|
} else {
|
||||||
updateTime: Date.now()
|
switch (err?.response?.data?.error?.code) {
|
||||||
});
|
case 'invalid_api_key':
|
||||||
|
errorText = 'API-KEY不合法';
|
||||||
|
break;
|
||||||
|
case 'context_length_exceeded':
|
||||||
|
errorText = '内容超长了,请重置对话';
|
||||||
|
break;
|
||||||
|
case 'rate_limit_reached':
|
||||||
|
errorText = '同时访问用户过多,请稍后再试';
|
||||||
|
break;
|
||||||
|
case null:
|
||||||
|
errorText = 'OpenAI 服务器访问超时';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorText = '服务器异常';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.error(errorText);
|
||||||
|
responseData.push(`event: serviceError\ndata: ${errorText}\n\n`);
|
||||||
|
|
||||||
res.end();
|
// 删除最一条数据库记录, 也就是预发送的那一条
|
||||||
|
(async () => {
|
||||||
|
await ChatWindow.findByIdAndUpdate(windowId, {
|
||||||
|
$pop: { content: 1 },
|
||||||
|
updateTime: Date.now()
|
||||||
|
});
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 开启 stream 传输
|
||||||
|
stream.pipe(res);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 安全校验
|
// 安全校验
|
||||||
if (chat.loadAmount === 0 || chat.expiredTime < Date.now()) {
|
if (!chat || chat.loadAmount === 0 || chat.expiredTime < Date.now()) {
|
||||||
throw new Error('聊天框已过期');
|
throw new Error('聊天框已过期');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
code: 500,
|
code: 500,
|
||||||
error: err
|
error: err
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|
||||||
if (req.method !== 'GET') return;
|
|
||||||
|
|
||||||
res.writeHead(200, {
|
|
||||||
Connection: 'keep-alive',
|
|
||||||
'Content-Encoding': 'none',
|
|
||||||
'Cache-Control': 'no-cache',
|
|
||||||
'Content-Type': 'text/event-stream'
|
|
||||||
});
|
|
||||||
|
|
||||||
let val = 0;
|
|
||||||
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
console.log('发送消息', val);
|
|
||||||
res.write(`data: ${val++}\n\n`);
|
|
||||||
if (val > 30) {
|
|
||||||
clearInterval(timer);
|
|
||||||
res.write(`data: [DONE]\n\n`);
|
|
||||||
res.end();
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,6 @@ import { useToast } from '@/hooks/useToast';
|
|||||||
import Icon from '@/components/Icon';
|
import Icon from '@/components/Icon';
|
||||||
import { useScreen } from '@/hooks/useScreen';
|
import { useScreen } from '@/hooks/useScreen';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useLoading } from '@/hooks/useLoading';
|
|
||||||
import { OpenAiModelEnum } from '@/constants/model';
|
import { OpenAiModelEnum } from '@/constants/model';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { useGlobalStore } from '@/store/global';
|
import { useGlobalStore } from '@/store/global';
|
||||||
@@ -75,9 +74,9 @@ const Chat = () => {
|
|||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
},
|
},
|
||||||
onError() {
|
onError(e: any) {
|
||||||
toast({
|
toast({
|
||||||
title: '初始化异常,请刷新',
|
title: e?.message || '初始化异常,请检查地址',
|
||||||
status: 'error',
|
status: 'error',
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
duration: 5000
|
duration: 5000
|
||||||
@@ -124,36 +123,55 @@ const Chat = () => {
|
|||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const event = getChatGPTSendEvent(chatId, windowId);
|
const event = getChatGPTSendEvent(chatId, windowId);
|
||||||
event.onmessage = ({ data }) => {
|
// 30s 收不到消息就报错
|
||||||
if (data === '[DONE]') {
|
let timer = setTimeout(() => {
|
||||||
event.close();
|
|
||||||
setChatList((state) =>
|
|
||||||
state.map((item, index) => {
|
|
||||||
if (index !== state.length - 1) return item;
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
status: 'finish'
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
resolve('');
|
|
||||||
} else if (data) {
|
|
||||||
const msg = data.replace(/<br\/>/g, '\n');
|
|
||||||
setChatList((state) =>
|
|
||||||
state.map((item, index) => {
|
|
||||||
if (index !== state.length - 1) return item;
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
value: item.value + msg
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
event.onerror = (err) => {
|
|
||||||
console.error(err, '===');
|
|
||||||
event.close();
|
event.close();
|
||||||
reject('对话出现错误');
|
reject('服务器超时');
|
||||||
|
}, 300000);
|
||||||
|
event.addEventListener('responseData', ({ data }) => {
|
||||||
|
/* 重置定时器 */
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
event.close();
|
||||||
|
reject('服务器超时');
|
||||||
|
}, 300000);
|
||||||
|
|
||||||
|
const msg = data.replace(/<br\/>/g, '\n');
|
||||||
|
setChatList((state) =>
|
||||||
|
state.map((item, index) => {
|
||||||
|
if (index !== state.length - 1) return item;
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
value: item.value + msg
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
event.addEventListener('done', () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
event.close();
|
||||||
|
setChatList((state) =>
|
||||||
|
state.map((item, index) => {
|
||||||
|
if (index !== state.length - 1) return item;
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
status: 'finish'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
resolve('');
|
||||||
|
});
|
||||||
|
event.addEventListener('serviceError', ({ data: err }) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
event.close();
|
||||||
|
console.error(err, '===');
|
||||||
|
reject(typeof err === 'string' ? err : '对话出现不知名错误~');
|
||||||
|
});
|
||||||
|
event.onerror = (err) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
event.close();
|
||||||
|
console.error(err);
|
||||||
|
reject(typeof err === 'string' ? err : '对话出现不知名错误~');
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -300,8 +318,8 @@ const Chat = () => {
|
|||||||
<Flex maxW={'800px'} m={'auto'} alignItems={'flex-start'}>
|
<Flex maxW={'800px'} m={'auto'} alignItems={'flex-start'}>
|
||||||
<Box mr={media(4, 1)}>
|
<Box mr={media(4, 1)}>
|
||||||
<Image
|
<Image
|
||||||
src={item.obj === 'Human' ? '/imgs/human.png' : '/imgs/modelAvatar.png'}
|
src={item.obj === 'Human' ? '/icon/human.png' : '/icon/logo.png'}
|
||||||
alt="/imgs/modelAvatar.png"
|
alt="/icon/logo.png"
|
||||||
width={30}
|
width={30}
|
||||||
height={30}
|
height={30}
|
||||||
/>
|
/>
|
||||||
@@ -320,6 +338,16 @@ const Chat = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
{/* 空内容提示 */}
|
||||||
|
{/* {
|
||||||
|
chatList.length === 0 && (
|
||||||
|
<>
|
||||||
|
<Card>
|
||||||
|
内容太长
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
} */}
|
||||||
<Box
|
<Box
|
||||||
m={media('20px auto', '0 auto')}
|
m={media('20px auto', '0 auto')}
|
||||||
w={media('100vw', '100%')}
|
w={media('100vw', '100%')}
|
||||||
|
|||||||
@@ -50,10 +50,6 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
async ({ email, code, password }: RegisterType) => {
|
async ({ email, code, password }: RegisterType) => {
|
||||||
setRequesting(true);
|
setRequesting(true);
|
||||||
try {
|
try {
|
||||||
toast({
|
|
||||||
title: `密码已找回`,
|
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
loginSuccess(
|
loginSuccess(
|
||||||
await postFindPassword({
|
await postFindPassword({
|
||||||
email,
|
email,
|
||||||
@@ -61,6 +57,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
password
|
password
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
toast({
|
||||||
|
title: `密码已找回`,
|
||||||
|
status: 'success'
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
typeof error === 'string' &&
|
typeof error === 'string' &&
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -32,16 +32,16 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
async ({ email, password }: LoginFormType) => {
|
async ({ email, password }: LoginFormType) => {
|
||||||
setRequesting(true);
|
setRequesting(true);
|
||||||
try {
|
try {
|
||||||
toast({
|
|
||||||
title: '登录成功',
|
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
loginSuccess(
|
loginSuccess(
|
||||||
await postLogin({
|
await postLogin({
|
||||||
email,
|
email,
|
||||||
password
|
password
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
toast({
|
||||||
|
title: '登录成功',
|
||||||
|
status: 'success'
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
typeof error === 'string' &&
|
typeof error === 'string' &&
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -50,10 +50,6 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
async ({ email, password, code }: RegisterType) => {
|
async ({ email, password, code }: RegisterType) => {
|
||||||
setRequesting(true);
|
setRequesting(true);
|
||||||
try {
|
try {
|
||||||
toast({
|
|
||||||
title: `注册成功`,
|
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
loginSuccess(
|
loginSuccess(
|
||||||
await postRegister({
|
await postRegister({
|
||||||
email,
|
email,
|
||||||
@@ -61,6 +57,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
password
|
password
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
toast({
|
||||||
|
title: `注册成功`,
|
||||||
|
status: 'success'
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
typeof error === 'string' &&
|
typeof error === 'string' &&
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
|
|||||||
status: 'success'
|
status: 'success'
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
toast({
|
toast({
|
||||||
title: err as string,
|
title: err as string,
|
||||||
status: 'success'
|
status: 'success'
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const Training = ({ model }: { model: ModelType }) => {
|
|||||||
const res = await getModelTrainings(id);
|
const res = await getModelTrainings(id);
|
||||||
setRecords(res);
|
setRecords(res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,8 @@ const ModelDetail = () => {
|
|||||||
const res = await getModelById(modelId as string);
|
const res = await getModelById(modelId as string);
|
||||||
res.security.expiredTime /= 60 * 60 * 1000;
|
res.security.expiredTime /= 60 * 60 * 1000;
|
||||||
setModel(res);
|
setModel(res);
|
||||||
|
|
||||||
console.log(res);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [modelId, setLoading]);
|
}, [modelId, setLoading]);
|
||||||
@@ -65,7 +63,7 @@ const ModelDetail = () => {
|
|||||||
});
|
});
|
||||||
router.replace('/model/list');
|
router.replace('/model/list');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [setLoading, model, router, toast]);
|
}, [setLoading, model, router, toast]);
|
||||||
@@ -79,7 +77,7 @@ const ModelDetail = () => {
|
|||||||
|
|
||||||
router.push(`/chat?chatId=${chatId}`);
|
router.push(`/chat?chatId=${chatId}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [setLoading, model, router]);
|
}, [setLoading, model, router]);
|
||||||
@@ -107,7 +105,7 @@ const ModelDetail = () => {
|
|||||||
title: typeof err === 'string' ? err : '文件格式错误',
|
title: typeof err === 'string' ? err : '文件格式错误',
|
||||||
status: 'error'
|
status: 'error'
|
||||||
});
|
});
|
||||||
console.log(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
},
|
},
|
||||||
@@ -123,7 +121,7 @@ const ModelDetail = () => {
|
|||||||
await putModelTrainingStatus(model._id);
|
await putModelTrainingStatus(model._id);
|
||||||
loadModel();
|
loadModel();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [setLoading, loadModel, model]);
|
}, [setLoading, loadModel, model]);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const ModelList = () => {
|
|||||||
shallow: true
|
shallow: true
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ export const jsonRes = (
|
|||||||
typeof error === 'string'
|
typeof error === 'string'
|
||||||
? error
|
? error
|
||||||
: openaiError[error?.response?.data?.message] || error?.message || '请求错误';
|
: openaiError[error?.response?.data?.message] || error?.message || '请求错误';
|
||||||
|
console.error(error);
|
||||||
console.log(msg);
|
console.error(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const sendCode = (email: string, code: string, type: `${EmailTypeEnum}`)
|
|||||||
};
|
};
|
||||||
mailTransport.sendMail(options, function (err, msg) {
|
mailTransport.sendMail(options, function (err, msg) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
reject('邮箱异常');
|
reject('邮箱异常');
|
||||||
} else {
|
} else {
|
||||||
resolve('');
|
resolve('');
|
||||||
@@ -53,7 +53,7 @@ export const sendTrainSucceed = (email: string, modelName: string) => {
|
|||||||
};
|
};
|
||||||
mailTransport.sendMail(options, function (err, msg) {
|
mailTransport.sendMail(options, function (err, msg) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.error(err);
|
||||||
reject('邮箱异常');
|
reject('邮箱异常');
|
||||||
} else {
|
} else {
|
||||||
resolve('');
|
resolve('');
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const useCopyData = () => {
|
|||||||
duration: 1000
|
duration: 1000
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
toast({
|
toast({
|
||||||
title: '复制失败',
|
title: '复制失败',
|
||||||
status: 'error'
|
status: 'error'
|
||||||
|
|||||||
Reference in New Issue
Block a user