feat: API access key management #2846 (#2865)

### 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:
balibabu
2024-10-16 15:57:39 +08:00
committed by GitHub
parent 4991107822
commit e5d3ab0332
32 changed files with 1303 additions and 165 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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>
</>
);

View File

@ -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;