mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? feat: API access key management #2846 feat: Render markdown file with remark-loader #2846 ### Type of change - [ ] Bug Fix (non-breaking change which fixes an issue) - [x] New Feature (non-breaking change which adds functionality) - [ ] Documentation Update - [ ] Refactoring - [ ] Performance Improvement - [ ] Other (please describe):
This commit is contained in:
6
web/src/assets/svg/api.svg
Normal file
6
web/src/assets/svg/api.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg t="1727608362384" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4306"
|
||||
width="24" height="24">
|
||||
<path
|
||||
d="M1019.520374 56.394706L966.393742 3.257862c-2.001695-2.001695-4.503815-2.87999-7.1387-2.879991s-5.137004 1.000848-7.148912 2.879991L856.749851 98.61414C814.520204 69.916363 765.396963 55.638964 716.283935 55.638964c-64.156382 0-128.312765 24.428855-177.30324 73.429543L411.280694 256.758295c-3.880838 3.880838-3.880838 10.274008 0 14.154847L751.74254 611.364776c2.001695 2.001695 4.514028 2.890203 7.1387 2.890203 2.512332 0 5.137004-1.000848 7.1387-2.890203l127.689788-127.689788c86.338436-86.460989 96.489892-220.166077 30.454367-317.646604l95.356279-95.366491c3.880838-3.993178 3.880838-10.386348 0-14.267187zM833.321844 423.409656l-74.430391 74.440604-234.075818-234.075818 74.430391-74.430391c31.199896-31.199896 72.806566-48.490052 117.037909-48.490051 44.231342 0 85.705247 17.167603 117.037909 48.490051 31.199896 31.199896 48.490052 72.806566 48.490051 117.037909 0 44.231342-17.290155 85.705247-48.490051 117.027696zM594.987317 554.980283c-3.880838-3.880838-10.274008-3.880838-14.154847 0l-83.458446 83.458446-113.15707-113.146858 83.580998-83.580999c3.891051-3.891051 3.891051-10.284221 0-14.165059l-45.610061-45.610061c-3.880838-3.891051-10.274008-3.891051-14.154846 0l-83.580999 83.580998-53.882374-53.882374c-2.001695-2.001695-4.514028-2.87999-7.1387-2.87999-2.512332 0-5.137004 1.000848-7.138699 2.87999L128.725037 539.313952C42.386601 625.774941 32.235145 759.480028 98.27067 856.970768L2.914392 952.33726c-3.880838 3.880838-3.880838 10.274008 0 14.154846l53.136844 53.126632c2.001695 2.001695 4.514028 2.87999 7.1387 2.87999 2.634885 0 5.137004-1.000848 7.1387-2.87999l95.366491-95.356279c42.219434 28.697777 91.352888 42.985389 140.465916 42.985389 64.156382 0 128.312765-24.439068 177.30324-73.429543l127.689789-127.689788c3.880838-3.891051 3.880838-10.274008 0-14.154847l-53.882374-53.882374 83.580998-83.580998c3.880838-3.880838 3.880838-10.274008 0-14.154847l-45.865379-45.375168zM423.066186 833.665314c-31.199896 31.199896-72.806566 48.490052-117.037908 48.490051-44.231342 0-85.705247-17.167603-117.037909-48.490051-31.199896-31.199896-48.490052-72.806566-48.490052-117.037909 0-44.231342 17.167603-85.71546 48.490052-117.037909l74.430391-74.430391 234.075817 234.075818-74.430391 74.430391z m0 0"
|
||||
fill="#2C2C2C" p-id="4307"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@ -12,9 +12,9 @@ const ChatApiKeyModal = ({
|
||||
dialogId,
|
||||
hideModal,
|
||||
idKey,
|
||||
}: IModalProps<any> & { dialogId: string; idKey: string }) => {
|
||||
}: IModalProps<any> & { dialogId?: string; idKey: string }) => {
|
||||
const { createToken, removeToken, tokenList, listLoading, creatingLoading } =
|
||||
useOperateApiKey(dialogId, idKey);
|
||||
useOperateApiKey(idKey, dialogId);
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
const columns: TableProps<IToken>['columns'] = [
|
||||
@ -36,9 +36,7 @@ const ChatApiKeyModal = ({
|
||||
render: (_, record) => (
|
||||
<Space size="middle">
|
||||
<CopyToClipboard text={record.token}></CopyToClipboard>
|
||||
<DeleteOutlined
|
||||
onClick={() => removeToken(record.token, record.tenant_id)}
|
||||
/>
|
||||
<DeleteOutlined onClick={() => removeToken(record.token)} />
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@ -60,8 +58,13 @@ const ChatApiKeyModal = ({
|
||||
dataSource={tokenList}
|
||||
rowKey={'token'}
|
||||
loading={listLoading}
|
||||
pagination={false}
|
||||
/>
|
||||
<Button onClick={createToken} loading={creatingLoading}>
|
||||
<Button
|
||||
onClick={createToken}
|
||||
loading={creatingLoading}
|
||||
disabled={tokenList.length > 0}
|
||||
>
|
||||
{t('createNewKey')}
|
||||
</Button>
|
||||
</Modal>
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
import HightLightMarkdown from '@/components/highlight-markdown';
|
||||
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
||||
import { Button, Card, Flex, Space } from 'antd';
|
||||
import apiDoc from '../../../../../docs/references/api.md';
|
||||
import ChatApiKeyModal from '../chat-api-key-modal';
|
||||
import EmbedModal from '../embed-modal';
|
||||
import { usePreviewChat, useShowEmbedModal } from '../hooks';
|
||||
import BackendServiceApi from './backend-service-api';
|
||||
|
||||
const ApiContent = ({
|
||||
id,
|
||||
idKey,
|
||||
hideChatPreviewCard = false,
|
||||
}: {
|
||||
id?: string;
|
||||
idKey: string;
|
||||
hideChatPreviewCard?: boolean;
|
||||
}) => {
|
||||
const { t } = useTranslate('chat');
|
||||
const {
|
||||
visible: apiKeyVisible,
|
||||
hideModal: hideApiKeyModal,
|
||||
showModal: showApiKeyModal,
|
||||
} = useSetModalState();
|
||||
const { embedVisible, hideEmbedModal, showEmbedModal, embedToken } =
|
||||
useShowEmbedModal(idKey, id);
|
||||
|
||||
const { handlePreview } = usePreviewChat(idKey, id);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Flex vertical gap={'middle'}>
|
||||
<BackendServiceApi show={showApiKeyModal}></BackendServiceApi>
|
||||
{!hideChatPreviewCard && (
|
||||
<Card title={`${name} Web App`}>
|
||||
<Flex gap={8} vertical>
|
||||
<Space size={'middle'}>
|
||||
<Button onClick={handlePreview}>{t('preview')}</Button>
|
||||
<Button onClick={showEmbedModal}>{t('embedded')}</Button>
|
||||
</Space>
|
||||
</Flex>
|
||||
</Card>
|
||||
)}
|
||||
<HightLightMarkdown>{apiDoc}</HightLightMarkdown>
|
||||
</Flex>
|
||||
{apiKeyVisible && (
|
||||
<ChatApiKeyModal
|
||||
hideModal={hideApiKeyModal}
|
||||
dialogId={id}
|
||||
idKey={idKey}
|
||||
></ChatApiKeyModal>
|
||||
)}
|
||||
{embedVisible && (
|
||||
<EmbedModal
|
||||
token={embedToken}
|
||||
visible={embedVisible}
|
||||
hideModal={hideEmbedModal}
|
||||
></EmbedModal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApiContent;
|
||||
@ -0,0 +1,35 @@
|
||||
import { Button, Card, Flex, Space, Typography } from 'antd';
|
||||
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import styles from './index.less';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const BackendServiceApi = ({ show }: { show(): void }) => {
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={
|
||||
<Space size={'large'}>
|
||||
<span>RAGFlow API</span>
|
||||
<Button onClick={show} type="primary">
|
||||
{t('apiKey')}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Flex gap={8} align="center">
|
||||
<b>{t('backendServiceApi')}</b>
|
||||
<Paragraph
|
||||
copyable={{ text: `${location.origin}/v1/api/` }}
|
||||
className={styles.apiLinkText}
|
||||
>
|
||||
{location.origin}/v1/api/
|
||||
</Paragraph>
|
||||
</Flex>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default BackendServiceApi;
|
||||
@ -13,9 +13,7 @@
|
||||
padding-left: 60px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.linkText {
|
||||
border-radius: 6px;
|
||||
padding: 6px 10px;
|
||||
background-color: #eff8ff;
|
||||
border: 1px;
|
||||
.apiLinkText {
|
||||
.linkText();
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
@ -1,65 +1,15 @@
|
||||
import LineChart from '@/components/line-chart';
|
||||
import { useFetchNextStats } from '@/hooks/chat-hooks';
|
||||
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { IStats } from '@/interfaces/database/chat';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
|
||||
import { RangePickerProps } from 'antd/es/date-picker';
|
||||
import dayjs from 'dayjs';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import ChatApiKeyModal from '../chat-api-key-modal';
|
||||
import EmbedModal from '../embed-modal';
|
||||
import {
|
||||
usePreviewChat,
|
||||
useSelectChartStatsList,
|
||||
useShowEmbedModal,
|
||||
} from '../hooks';
|
||||
import styles from './index.less';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
const StatsLineChart = ({ statsType }: { statsType: keyof IStats }) => {
|
||||
const { t } = useTranslate('chat');
|
||||
const chartList = useSelectChartStatsList();
|
||||
const list =
|
||||
chartList[statsType]?.map((x) => ({
|
||||
...x,
|
||||
xAxis: formatDate(x.xAxis),
|
||||
})) ?? [];
|
||||
|
||||
return (
|
||||
<div className={styles.chartItem}>
|
||||
<b className={styles.chartLabel}>{t(camelCase(statsType))}</b>
|
||||
<LineChart data={list}></LineChart>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
import { Modal } from 'antd';
|
||||
import ApiContent from './api-content';
|
||||
|
||||
const ChatOverviewModal = ({
|
||||
visible,
|
||||
hideModal,
|
||||
id,
|
||||
name = '',
|
||||
idKey,
|
||||
}: IModalProps<any> & { id: string; name?: string; idKey: string }) => {
|
||||
const { t } = useTranslate('chat');
|
||||
const {
|
||||
visible: apiKeyVisible,
|
||||
hideModal: hideApiKeyModal,
|
||||
showModal: showApiKeyModal,
|
||||
} = useSetModalState();
|
||||
const { embedVisible, hideEmbedModal, showEmbedModal, embedToken } =
|
||||
useShowEmbedModal(id, idKey);
|
||||
|
||||
const { pickerValue, setPickerValue } = useFetchNextStats();
|
||||
|
||||
const disabledDate: RangePickerProps['disabledDate'] = (current) => {
|
||||
return current && current > dayjs().endOf('day');
|
||||
};
|
||||
|
||||
const { handlePreview } = usePreviewChat(id, idKey);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -72,69 +22,7 @@ const ChatOverviewModal = ({
|
||||
width={'100vw'}
|
||||
okText={t('close', { keyPrefix: 'common' })}
|
||||
>
|
||||
<Flex vertical gap={'middle'}>
|
||||
<Card title={t('backendServiceApi')}>
|
||||
<Flex gap={8} vertical>
|
||||
{t('serviceApiEndpoint')}
|
||||
<Paragraph
|
||||
copyable={{ text: `${location.origin}/v1/api/` }}
|
||||
className={styles.linkText}
|
||||
>
|
||||
{location.origin}/v1/api/
|
||||
</Paragraph>
|
||||
</Flex>
|
||||
<Space size={'middle'}>
|
||||
<Button onClick={showApiKeyModal}>{t('apiKey')}</Button>
|
||||
<a
|
||||
href={
|
||||
'https://github.com/infiniflow/ragflow/blob/main/docs/references/api.md'
|
||||
}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Button>{t('apiReference')}</Button>
|
||||
</a>
|
||||
</Space>
|
||||
</Card>
|
||||
<Card title={`${name} Web App`}>
|
||||
<Flex gap={8} vertical>
|
||||
<Space size={'middle'}>
|
||||
<Button onClick={handlePreview}>{t('preview')}</Button>
|
||||
<Button onClick={showEmbedModal}>{t('embedded')}</Button>
|
||||
</Space>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
||||
<Space>
|
||||
<b>{t('dateRange')}</b>
|
||||
<RangePicker
|
||||
disabledDate={disabledDate}
|
||||
value={pickerValue}
|
||||
onChange={setPickerValue}
|
||||
allowClear={false}
|
||||
/>
|
||||
</Space>
|
||||
<div className={styles.chartWrapper}>
|
||||
<StatsLineChart statsType={'pv'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'round'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'speed'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'thumb_up'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'tokens'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'uv'}></StatsLineChart>
|
||||
</div>
|
||||
</Flex>
|
||||
{apiKeyVisible && (
|
||||
<ChatApiKeyModal
|
||||
hideModal={hideApiKeyModal}
|
||||
dialogId={id}
|
||||
idKey={idKey}
|
||||
></ChatApiKeyModal>
|
||||
)}
|
||||
<EmbedModal
|
||||
token={embedToken}
|
||||
visible={embedVisible}
|
||||
hideModal={hideEmbedModal}
|
||||
></EmbedModal>
|
||||
<ApiContent id={id} idKey={idKey}></ApiContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
import LineChart from '@/components/line-chart';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IStats } from '@/interfaces/database/chat';
|
||||
import { formatDate } from '@/utils/date';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import { useSelectChartStatsList } from '../hooks';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const StatsLineChart = ({ statsType }: { statsType: keyof IStats }) => {
|
||||
const { t } = useTranslate('chat');
|
||||
const chartList = useSelectChartStatsList();
|
||||
const list =
|
||||
chartList[statsType]?.map((x) => ({
|
||||
...x,
|
||||
xAxis: formatDate(x.xAxis),
|
||||
})) ?? [];
|
||||
|
||||
return (
|
||||
<div className={styles.chartItem}>
|
||||
<b className={styles.chartLabel}>{t(camelCase(statsType))}</b>
|
||||
<LineChart data={list}></LineChart>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const StatsChart = () => {
|
||||
return (
|
||||
<div className={styles.chartWrapper}>
|
||||
<StatsLineChart statsType={'pv'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'round'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'speed'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'thumb_up'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'tokens'}></StatsLineChart>
|
||||
<StatsLineChart statsType={'uv'}></StatsLineChart>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatsChart;
|
||||
@ -1,31 +1,31 @@
|
||||
import { SharedFrom } from '@/constants/chat';
|
||||
import {
|
||||
useCreateNextToken,
|
||||
useFetchTokenList,
|
||||
useRemoveNextToken,
|
||||
} from '@/hooks/chat-hooks';
|
||||
import {
|
||||
useSetModalState,
|
||||
useShowDeleteConfirm,
|
||||
useTranslate,
|
||||
} from '@/hooks/common-hooks';
|
||||
import {
|
||||
useCreateSystemToken,
|
||||
useFetchSystemTokenList,
|
||||
useRemoveSystemToken,
|
||||
} from '@/hooks/user-setting-hooks';
|
||||
import { IStats } from '@/interfaces/database/chat';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { message } from 'antd';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useOperateApiKey = (dialogId: string, idKey: string) => {
|
||||
const { removeToken } = useRemoveNextToken();
|
||||
const { createToken, loading: creatingLoading } = useCreateNextToken();
|
||||
const { data: tokenList, loading: listLoading } = useFetchTokenList({
|
||||
export const useOperateApiKey = (idKey: string, dialogId?: string) => {
|
||||
const { removeToken } = useRemoveSystemToken();
|
||||
const { createToken, loading: creatingLoading } = useCreateSystemToken();
|
||||
const { data: tokenList, loading: listLoading } = useFetchSystemTokenList({
|
||||
[idKey]: dialogId,
|
||||
});
|
||||
|
||||
const showDeleteConfirm = useShowDeleteConfirm();
|
||||
|
||||
const onRemoveToken = (token: string, tenantId: string) => {
|
||||
const onRemoveToken = (token: string) => {
|
||||
showDeleteConfirm({
|
||||
onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
|
||||
onOk: () => removeToken(token),
|
||||
});
|
||||
};
|
||||
|
||||
@ -49,7 +49,7 @@ type ChartStatsType = {
|
||||
export const useSelectChartStatsList = (): ChartStatsType => {
|
||||
const queryClient = useQueryClient();
|
||||
const data = queryClient.getQueriesData({ queryKey: ['fetchStats'] });
|
||||
const stats: IStats = data[0][1] as IStats;
|
||||
const stats: IStats = (data.length > 0 ? data[0][1] : {}) as IStats;
|
||||
|
||||
return Object.keys(stats).reduce((pre, cur) => {
|
||||
const item = stats[cur as keyof IStats];
|
||||
@ -77,10 +77,12 @@ const getUrlWithToken = (token: string, from: string = 'chat') => {
|
||||
return `${protocol}//${host}/chat/share?shared_id=${token}&from=${from}`;
|
||||
};
|
||||
|
||||
const useFetchTokenListBeforeOtherStep = (dialogId: string, idKey: string) => {
|
||||
const useFetchTokenListBeforeOtherStep = (idKey: string, dialogId?: string) => {
|
||||
const { showTokenEmptyError } = useShowTokenEmptyError();
|
||||
|
||||
const { data: tokenList, refetch } = useFetchTokenList({ [idKey]: dialogId });
|
||||
const { data: tokenList, refetch } = useFetchSystemTokenList({
|
||||
[idKey]: dialogId,
|
||||
});
|
||||
|
||||
const token =
|
||||
Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
|
||||
@ -102,7 +104,7 @@ const useFetchTokenListBeforeOtherStep = (dialogId: string, idKey: string) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useShowEmbedModal = (dialogId: string, idKey: string) => {
|
||||
export const useShowEmbedModal = (idKey: string, dialogId?: string) => {
|
||||
const {
|
||||
visible: embedVisible,
|
||||
hideModal: hideEmbedModal,
|
||||
@ -110,8 +112,8 @@ export const useShowEmbedModal = (dialogId: string, idKey: string) => {
|
||||
} = useSetModalState();
|
||||
|
||||
const { handleOperate, token } = useFetchTokenListBeforeOtherStep(
|
||||
dialogId,
|
||||
idKey,
|
||||
dialogId,
|
||||
);
|
||||
|
||||
const handleShowEmbedModal = useCallback(async () => {
|
||||
@ -129,8 +131,8 @@ export const useShowEmbedModal = (dialogId: string, idKey: string) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const usePreviewChat = (dialogId: string, idKey: string) => {
|
||||
const { handleOperate } = useFetchTokenListBeforeOtherStep(dialogId, idKey);
|
||||
export const usePreviewChat = (idKey: string, dialogId?: string) => {
|
||||
const { handleOperate } = useFetchTokenListBeforeOtherStep(idKey, dialogId);
|
||||
|
||||
const open = useCallback(
|
||||
(t: string) => {
|
||||
|
||||
@ -1,4 +1,19 @@
|
||||
.text {
|
||||
white-space: pre-wrap; // https://stackoverflow.com/questions/60332183/new-line-with-react-markdown
|
||||
.chunkText;
|
||||
font-size: 16px;
|
||||
li {
|
||||
padding: 4px 0px;
|
||||
}
|
||||
p {
|
||||
white-space: pre-wrap; // https://stackoverflow.com/questions/60332183/new-line-with-react-markdown
|
||||
}
|
||||
}
|
||||
|
||||
.code {
|
||||
padding: 3px 6px 6px;
|
||||
margin: 0;
|
||||
white-space: break-spaces;
|
||||
background-color: rgba(129, 139, 152, 0.12);
|
||||
border-radius: 4px;
|
||||
color: rgb(31, 35, 40);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames';
|
||||
import Markdown from 'react-markdown';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
@ -22,11 +22,16 @@ const HightLightMarkdown = ({
|
||||
const { children, className, node, ...rest } = props;
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
return match ? (
|
||||
<SyntaxHighlighter {...rest} PreTag="div" language={match[1]}>
|
||||
<SyntaxHighlighter
|
||||
{...rest}
|
||||
PreTag="div"
|
||||
language={match[1]}
|
||||
// style={dark}
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
</SyntaxHighlighter>
|
||||
) : (
|
||||
<code {...rest} className={className}>
|
||||
<code {...rest} className={`${className} ${styles.code}`}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
|
||||
@ -41,6 +41,12 @@ export const fileIconMap = {
|
||||
|
||||
export const LanguageList = ['English', 'Chinese', 'Traditional Chinese'];
|
||||
|
||||
export const LanguageMap = {
|
||||
English: 'English',
|
||||
Chinese: '简体中文',
|
||||
'Traditional Chinese': '繁體中文',
|
||||
};
|
||||
|
||||
export const LanguageTranslationMap = {
|
||||
English: 'en',
|
||||
Chinese: 'zh',
|
||||
|
||||
@ -5,6 +5,7 @@ export enum UserSettingRouteKey {
|
||||
Password = 'password',
|
||||
Model = 'model',
|
||||
System = 'system',
|
||||
Api = 'api',
|
||||
Team = 'team',
|
||||
Logout = 'logout',
|
||||
}
|
||||
|
||||
@ -441,7 +441,7 @@ export const useRemoveNextToken = () => {
|
||||
mutationKey: ['removeToken'],
|
||||
mutationFn: async (params: {
|
||||
tenantId: string;
|
||||
dialogId: string;
|
||||
dialogId?: string;
|
||||
tokens: string[];
|
||||
}) => {
|
||||
const { data } = await chatService.removeToken(params);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { LanguageTranslationMap } from '@/constants/common';
|
||||
import { ResponseGetType } from '@/interfaces/database/base';
|
||||
import { IToken } from '@/interfaces/database/chat';
|
||||
import { ITenantInfo } from '@/interfaces/database/knowledge';
|
||||
import { ISystemStatus, IUserInfo } from '@/interfaces/database/user-setting';
|
||||
import userService from '@/services/user-service';
|
||||
@ -152,3 +153,65 @@ export const useFetchSystemStatus = () => {
|
||||
loading,
|
||||
};
|
||||
};
|
||||
|
||||
export const useFetchSystemTokenList = (params: Record<string, any>) => {
|
||||
const {
|
||||
data,
|
||||
isFetching: loading,
|
||||
refetch,
|
||||
} = useQuery<IToken[]>({
|
||||
queryKey: ['fetchSystemTokenList', params],
|
||||
initialData: [],
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
const { data } = await userService.listToken(params);
|
||||
|
||||
return data?.data ?? [];
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, refetch };
|
||||
};
|
||||
|
||||
export const useRemoveSystemToken = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: ['removeSystemToken'],
|
||||
mutationFn: async (token: string) => {
|
||||
const { data } = await userService.removeToken({}, token);
|
||||
if (data.retcode === 0) {
|
||||
message.success(t('message.deleted'));
|
||||
queryClient.invalidateQueries({ queryKey: ['fetchSystemTokenList'] });
|
||||
}
|
||||
return data?.data ?? [];
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, removeToken: mutateAsync };
|
||||
};
|
||||
|
||||
export const useCreateSystemToken = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
data,
|
||||
isPending: loading,
|
||||
mutateAsync,
|
||||
} = useMutation({
|
||||
mutationKey: ['createSystemToken'],
|
||||
mutationFn: async (params: Record<string, any>) => {
|
||||
const { data } = await userService.createToken(params);
|
||||
if (data.retcode === 0) {
|
||||
queryClient.invalidateQueries({ queryKey: ['fetchSystemTokenList'] });
|
||||
}
|
||||
return data?.data ?? [];
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading, createToken: mutateAsync };
|
||||
};
|
||||
|
||||
@ -5,7 +5,7 @@ import camelCase from 'lodash/camelCase';
|
||||
import React from 'react';
|
||||
import User from '../user';
|
||||
|
||||
import { LanguageList } from '@/constants/common';
|
||||
import { LanguageList, LanguageMap } from '@/constants/common';
|
||||
import { useChangeLanguage } from '@/hooks/logic-hooks';
|
||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||
import styled from './index.less';
|
||||
@ -35,7 +35,7 @@ const RightToolBar = () => {
|
||||
|
||||
const items: MenuProps['items'] = LanguageList.map((x) => ({
|
||||
key: x,
|
||||
label: <span>{t(camelCase(x))}</span>,
|
||||
label: <span>{LanguageMap[x as keyof typeof LanguageMap]}</span>,
|
||||
})).reduce<MenuProps['items']>((pre, cur) => {
|
||||
return [...pre!, { type: 'divider' }, cur];
|
||||
}, []);
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
.tableCell() {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid #d1d9e0;
|
||||
}
|
||||
|
||||
.chunkText() {
|
||||
em {
|
||||
color: red;
|
||||
@ -5,6 +10,8 @@
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
@ -17,8 +24,13 @@
|
||||
}
|
||||
|
||||
th {
|
||||
color: #fff;
|
||||
background-color: @blurBackground;
|
||||
// color: #fff;
|
||||
// background-color: @blurBackground;
|
||||
.tableCell;
|
||||
}
|
||||
|
||||
td {
|
||||
.tableCell;
|
||||
}
|
||||
|
||||
td:hover {
|
||||
@ -56,3 +68,10 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.linkText() {
|
||||
border-radius: 6px;
|
||||
padding: 6px 10px;
|
||||
background-color: #eff8ff;
|
||||
border: 1px;
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ The above is the content you need to summarize.`,
|
||||
quoteTip: 'Should the source of the original text be displayed?',
|
||||
selfRag: 'Self-RAG',
|
||||
selfRagTip: 'Please refer to: https://huggingface.co/papers/2310.11511',
|
||||
overview: 'Chat Bot API',
|
||||
overview: 'Chat ID',
|
||||
pv: 'Number of messages',
|
||||
uv: 'Active user number',
|
||||
speed: 'Token output speed',
|
||||
@ -413,7 +413,7 @@ The above is the content you need to summarize.`,
|
||||
apiKey: 'API Key',
|
||||
apiReference: 'API Documents',
|
||||
dateRange: 'Date Range:',
|
||||
backendServiceApi: 'Backend service API',
|
||||
backendServiceApi: 'API Server',
|
||||
createNewKey: 'Create new key',
|
||||
created: 'Created',
|
||||
action: 'Action',
|
||||
@ -451,6 +451,7 @@ The above is the content you need to summarize.`,
|
||||
team: 'Team',
|
||||
system: 'System',
|
||||
logout: 'Log out',
|
||||
api: 'API',
|
||||
username: 'Username',
|
||||
usernameMessage: 'Please input your username!',
|
||||
photo: 'Your photo',
|
||||
|
||||
@ -371,7 +371,7 @@ export default {
|
||||
quoteTip: '是否應該顯示原文出處?',
|
||||
selfRag: 'Self-RAG',
|
||||
selfRagTip: '請參考: https://huggingface.co/papers/2310.11511',
|
||||
overview: '聊天 API',
|
||||
overview: '聊天 ID',
|
||||
pv: '消息數',
|
||||
uv: '活躍用戶數',
|
||||
speed: 'Token 輸出速度',
|
||||
@ -384,7 +384,7 @@ export default {
|
||||
apiKey: 'API 鍵',
|
||||
apiReference: 'API 文檔',
|
||||
dateRange: '日期範圍:',
|
||||
backendServiceApi: '後端服務 API',
|
||||
backendServiceApi: 'API 伺服器',
|
||||
createNewKey: '創建新密鑰',
|
||||
created: '創建於',
|
||||
action: '操作',
|
||||
|
||||
@ -388,7 +388,7 @@ export default {
|
||||
quoteTip: '是否应该显示原文出处?',
|
||||
selfRag: 'Self-RAG',
|
||||
selfRagTip: '请参考: https://huggingface.co/papers/2310.11511',
|
||||
overview: '聊天 API',
|
||||
overview: '聊天 ID',
|
||||
pv: '消息数',
|
||||
uv: '活跃用户数',
|
||||
speed: 'Token 输出速度',
|
||||
@ -401,7 +401,7 @@ export default {
|
||||
apiKey: 'API 键',
|
||||
apiReference: 'API 文档',
|
||||
dateRange: '日期范围:',
|
||||
backendServiceApi: '后端服务 API',
|
||||
backendServiceApi: 'API 服务器',
|
||||
createNewKey: '创建新密钥',
|
||||
created: '创建于',
|
||||
action: '操作',
|
||||
|
||||
11
web/src/pages/api/index.tsx
Normal file
11
web/src/pages/api/index.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import ApiContent from '@/components/api-service/chat-overview-modal/api-content';
|
||||
|
||||
const ApiPage = () => {
|
||||
return (
|
||||
<div>
|
||||
<ApiContent idKey="dialogId"></ApiContent>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApiPage;
|
||||
3
web/src/pages/chat/chat-id-modal/index.less
Normal file
3
web/src/pages/chat/chat-id-modal/index.less
Normal file
@ -0,0 +1,3 @@
|
||||
.id {
|
||||
.linkText();
|
||||
}
|
||||
34
web/src/pages/chat/chat-id-modal/index.tsx
Normal file
34
web/src/pages/chat/chat-id-modal/index.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { Modal, Typography } from 'antd';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
|
||||
const ChatIdModal = ({
|
||||
visible,
|
||||
hideModal,
|
||||
id,
|
||||
}: IModalProps<any> & { id: string; name?: string; idKey: string }) => {
|
||||
const { t } = useTranslate('chat');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title={t('overview')}
|
||||
open={visible}
|
||||
onCancel={hideModal}
|
||||
cancelButtonProps={{ style: { display: 'none' } }}
|
||||
onOk={hideModal}
|
||||
okText={t('close', { keyPrefix: 'common' })}
|
||||
>
|
||||
<Paragraph copyable={{ text: id }} className={styles.id}>
|
||||
{id}
|
||||
</Paragraph>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatIdModal;
|
||||
@ -1,6 +1,6 @@
|
||||
import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
|
||||
import RenameModal from '@/components/rename-modal';
|
||||
import { CloudOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import { DeleteOutlined, EditOutlined, KeyOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
@ -29,7 +29,6 @@ import {
|
||||
useSelectDerivedConversationList,
|
||||
} from './hooks';
|
||||
|
||||
import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
|
||||
import SvgIcon from '@/components/svg-icon';
|
||||
import {
|
||||
useClickConversationCard,
|
||||
@ -40,6 +39,7 @@ import {
|
||||
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
|
||||
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
|
||||
import { IDialog } from '@/interfaces/database/chat';
|
||||
import ChatIdModal from './chat-id-modal';
|
||||
import styles from './index.less';
|
||||
|
||||
const { Text } = Typography;
|
||||
@ -190,7 +190,7 @@ const Chat = () => {
|
||||
onClick: handleShowOverviewModal(dialog),
|
||||
label: (
|
||||
<Space>
|
||||
<CloudOutlined />
|
||||
<KeyOutlined />
|
||||
{t('overview')}
|
||||
</Space>
|
||||
),
|
||||
@ -367,13 +367,13 @@ const Chat = () => {
|
||||
loading={conversationRenameLoading}
|
||||
></RenameModal>
|
||||
{overviewVisible && (
|
||||
<ChatOverviewModal
|
||||
<ChatIdModal
|
||||
visible={overviewVisible}
|
||||
hideModal={hideOverviewModal}
|
||||
id={currentRecord.id}
|
||||
name={currentRecord.name}
|
||||
idKey="dialogId"
|
||||
></ChatOverviewModal>
|
||||
></ChatIdModal>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { ReactComponent as ApiIcon } from '@/assets/svg/api.svg';
|
||||
import { ReactComponent as LogoutIcon } from '@/assets/svg/logout.svg';
|
||||
import { ReactComponent as ModelIcon } from '@/assets/svg/model-providers.svg';
|
||||
import { ReactComponent as PasswordIcon } from '@/assets/svg/password.svg';
|
||||
@ -13,6 +14,7 @@ export const UserSettingIconMap = {
|
||||
[UserSettingRouteKey.System]: <MonitorOutlined style={{ fontSize: 24 }} />,
|
||||
[UserSettingRouteKey.Team]: <TeamIcon />,
|
||||
[UserSettingRouteKey.Logout]: <LogoutIcon />,
|
||||
[UserSettingRouteKey.Api]: <ApiIcon />,
|
||||
};
|
||||
|
||||
export * from '@/constants/setting';
|
||||
|
||||
7
web/src/pages/user-setting/setting-api/index.less
Normal file
7
web/src/pages/user-setting/setting-api/index.less
Normal file
@ -0,0 +1,7 @@
|
||||
.apiWrapper {
|
||||
width: 100%;
|
||||
|
||||
div[class^='chartWrapper'] {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
13
web/src/pages/user-setting/setting-api/index.tsx
Normal file
13
web/src/pages/user-setting/setting-api/index.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import ApiContent from '@/components/api-service/chat-overview-modal/api-content';
|
||||
|
||||
import styles from './index.less';
|
||||
|
||||
const ApiPage = () => {
|
||||
return (
|
||||
<div className={styles.apiWrapper}>
|
||||
<ApiContent idKey="dialogId" hideChatPreviewCard></ApiContent>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApiPage;
|
||||
@ -77,6 +77,10 @@ const routes = [
|
||||
path: '/user-setting/system',
|
||||
component: '@/pages/user-setting/setting-system',
|
||||
},
|
||||
{
|
||||
path: '/user-setting/api',
|
||||
component: '@/pages/user-setting/setting-api',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@ -19,6 +19,9 @@ const {
|
||||
deleteFactory,
|
||||
getSystemStatus,
|
||||
getSystemVersion,
|
||||
getSystemTokenList,
|
||||
removeSystemToken,
|
||||
createSystemToken,
|
||||
} = api;
|
||||
|
||||
const methods = {
|
||||
@ -86,6 +89,18 @@ const methods = {
|
||||
url: deleteFactory,
|
||||
method: 'post',
|
||||
},
|
||||
listToken: {
|
||||
url: getSystemTokenList,
|
||||
method: 'get',
|
||||
},
|
||||
createToken: {
|
||||
url: createSystemToken,
|
||||
method: 'post',
|
||||
},
|
||||
removeToken: {
|
||||
url: removeSystemToken,
|
||||
method: 'delete',
|
||||
},
|
||||
} as const;
|
||||
|
||||
const userService = registerServer<keyof typeof methods>(methods, request);
|
||||
|
||||
@ -94,6 +94,10 @@ export default {
|
||||
// system
|
||||
getSystemVersion: `${api_host}/system/version`,
|
||||
getSystemStatus: `${api_host}/system/status`,
|
||||
getSystemTokenList: `${api_host}/system/token_list`,
|
||||
createSystemToken: `${api_host}/system/new_token`,
|
||||
listSystemToken: `${api_host}/system/token_list`,
|
||||
removeSystemToken: `${api_host}/system/token`,
|
||||
|
||||
// flow
|
||||
listTemplates: `${api_host}/canvas/templates`,
|
||||
|
||||
Reference in New Issue
Block a user