feat(web): create api support rate_limit & daily_request_limit config

This commit is contained in:
zhaoying
2026-04-22 22:03:31 +08:00
parent 6f323f2435
commit 2a514a9e04
5 changed files with 140 additions and 54 deletions

View File

@@ -17,6 +17,7 @@
import { type FC, type ReactNode, useEffect, useState } from 'react';
import { Slider, type SliderSingleProps, Flex, InputNumber, type InputNumberProps } from 'antd';
import { useTranslation } from 'react-i18next';
/** Props interface for RbSlider component */
interface RbSliderProps extends SliderSingleProps {
@@ -41,12 +42,13 @@ const RbSlider: FC<RbSliderProps> = ({
step = 0.01,
size = 'default' ,
isInput = false,
className = '',
className = 'rb:pl-1!',
prefix,
inputClassName,
disabled,
...rest
}) => {
const { t } = useTranslation()
const [curValue, setCurValue] = useState<SliderSingleProps['value']>(0)
useEffect(() => {
setCurValue(value)
@@ -102,6 +104,7 @@ const RbSlider: FC<RbSliderProps> = ({
onChange={handleInputChange}
prefix={prefix}
className={`${inputClassName || '' } rb:w-20!`}
placeholder={t('common.pleaseEnter')}
/>
: <div className="rb:text-[14px] rb:text-[#155EEF] rb:leading-5">{curValue || min}</div>
}

View File

@@ -106,16 +106,28 @@ const ApiKeyDetailModal = forwardRef<ApiKeyModalRef, { handleCopy: (content: str
</span>
</div>
{data.expires_at && <>
<div className="rb:text-[#5B6167] rb:font-medium rb:leading-5 rb:my-4">{t('apiKey.advancedSettings')}</div>
{data.expires_at &&
<div className="rb:flex rb:justify-between rb:gap-5 rb:font-regular rb:text-[14px] rb:mt-3">
<span className="rb:text-[#5B6167]">{t(`apiKey.expires_at`)}</span>
<span>
{data.expires_at ? formatDateTime(data.expires_at as number, 'YYYY-MM-DD HH:mm:ss') : '-'}
</span>
</div>
</>}
}
<div className="rb:flex rb:justify-between rb:gap-5 rb:font-regular rb:text-[14px] rb:mt-3">
<span className="rb:text-[#5B6167]">{t(`application.qpsLimit`)}</span>
<span>
{data.rate_limit} {t('application.qpsLimitUnit')}
</span>
</div>
<div className="rb:flex rb:justify-between rb:gap-5 rb:font-regular rb:text-[14px] rb:mt-3">
<span className="rb:text-[#5B6167]">{t(`application.dailyUsageLimit`)}</span>
<span>
{data.daily_request_limit} {t('application.dailyUsageLimitUnit')}
</span>
</div>
</RbModal>
);
});

View File

@@ -13,6 +13,7 @@ import type { ApiKey, ApiKeyModalRef } from '../types';
import RbModal from '@/components/RbModal'
import { createApiKey, updateApiKey } from '@/api/apiKey';
import { stringRegExp } from '@/utils/validator';
import RbSlider from '@/components/RbSlider'
const FormItem = Form.Item;
@@ -57,11 +58,10 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, CreateModalProps>(({
*/
const handleOpen = (apiKey?: ApiKey) => {
if (apiKey?.id) {
const { scopes = [], expires_at } = apiKey
const { scopes = [], expires_at, ...rest } = apiKey
// Edit mode - populate form with existing data
form.setFieldsValue({
name: apiKey.name,
description: apiKey.description,
...rest,
memory: scopes.includes('memory'),
rag: scopes.includes('rag'),
expires_at: expires_at ? dayjs(expires_at) : undefined
@@ -126,6 +126,10 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, CreateModalProps>(({
<Form
form={form}
layout="vertical"
initialValues={{
rate_limit: 50,
daily_request_limit: 100000
}}
>
<div className="rb:text-[#5B6167] rb:font-medium rb:leading-5 rb:mb-4">{t('apiKey.baseInfo')}</div>
<FormItem
@@ -179,6 +183,36 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, CreateModalProps>(({
disabledDate={(current) => current && current < dayjs().subtract(1, 'day').endOf('day')}
/>
</FormItem>
<FormItem
name="rate_limit"
label={<>{t(`application.qpsLimit`)}({t('application.qpsLimitTip')}, {t('application.qpsLimitUnit')})</>}
extra={t('application.qpsLimitDesc')}
rules={[
{ required: true, message: t('common.pleaseEnter') },
]}
>
<RbSlider
min={1}
max={100}
step={1}
isInput={true}
/>
</FormItem>
<FormItem
name="daily_request_limit"
label={<>{t(`application.dailyUsageLimit`)} ({t('application.dailyUsageLimitUnit')})</>}
extra={t('application.dailyUsageLimitDesc')}
rules={[
{ required: true, message: t('common.pleaseEnter') },
]}
>
<RbSlider
min={100}
max={100000}
step={100}
isInput={true}
/>
</FormItem>
</Form>
</RbModal>
);

View File

@@ -195,6 +195,7 @@ const Api: FC<{ application: Application | null }> = ({ application }) => {
</Col>
</Row>
{/* API Key List */}
<Flex vertical gap={12}>
{apiKeyList.sort((a, b) => b.created_at - a.created_at).map(item => (
<div key={item.id} className="rb:p-4 rb-border rb:rounded-xl">
<Flex align="center" justify="space-between">
@@ -242,6 +243,7 @@ const Api: FC<{ application: Application | null }> = ({ application }) => {
</Row>
</div>
))}
</Flex>
</RbCard>
</Flex>

View File

@@ -17,6 +17,7 @@ import type { Application } from '@/views/ApplicationManagement/types'
import type { ApiKeyModalRef } from '../types'
import { createApiKey } from '@/api/apiKey';
import RbModal from '@/components/RbModal'
import RbSlider from '@/components/RbSlider'
const FormItem = Form.Item;
@@ -97,6 +98,10 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
form={form}
layout="vertical"
scrollToFirstError={{ behavior: 'instant', block: 'end', focus: true }}
initialValues={{
rate_limit: 50,
daily_request_limit: 100000
}}
>
{/* Key name */}
<FormItem
@@ -116,6 +121,36 @@ const ApiKeyModal = forwardRef<ApiKeyModalRef, ApiKeyModalProps>(({
>
<Input.TextArea placeholder={t('application.apiKeyDescPlaceholder')} />
</FormItem>
<FormItem
name="rate_limit"
label={<>{t(`application.qpsLimit`)}({t('application.qpsLimitTip')}, {t('application.qpsLimitUnit')})</>}
extra={t('application.qpsLimitDesc')}
rules={[
{ required: true, message: t('common.pleaseEnter') },
]}
>
<RbSlider
min={1}
max={100}
step={1}
isInput={true}
/>
</FormItem>
<FormItem
name="daily_request_limit"
label={<>{t(`application.dailyUsageLimit`)} ({t('application.dailyUsageLimitUnit')})</>}
extra={t('application.dailyUsageLimitDesc')}
rules={[
{ required: true, message: t('common.pleaseEnter') },
]}
>
<RbSlider
min={100}
max={100000}
step={100}
isInput={true}
/>
</FormItem>
</Form>
</RbModal>
);