diff --git a/web/src/components/RateLimitSetting.js b/web/src/components/RateLimitSetting.js index 309b94de..4671317f 100644 --- a/web/src/components/RateLimitSetting.js +++ b/web/src/components/RateLimitSetting.js @@ -13,7 +13,7 @@ const RateLimitSetting = () => { ModelRequestRateLimitCount: 0, ModelRequestRateLimitSuccessCount: 1000, ModelRequestRateLimitDurationMinutes: 1, - ModelRequestRateLimitGroup: '{}', + ModelRequestRateLimitGroup: '', }); let [loading, setLoading] = useState(false); @@ -24,12 +24,14 @@ const RateLimitSetting = () => { if (success) { let newInputs = {}; data.forEach((item) => { - if (Object.prototype.hasOwnProperty.call(inputs, item.key)) { - if (item.key.endsWith('Enabled')) { - newInputs[item.key] = item.value === 'true'; - } else { - newInputs[item.key] = item.value; - } + if (item.key === 'ModelRequestRateLimitGroup') { + item.value = JSON.stringify(JSON.parse(item.value), null, 2); + } + + if (item.key.endsWith('Enabled')) { + newInputs[item.key] = item.value === 'true' ? true : false; + } else { + newInputs[item.key] = item.value; } }); diff --git a/web/src/pages/Setting/RateLimit/SettingsRequestRateLimit.js b/web/src/pages/Setting/RateLimit/SettingsRequestRateLimit.js index 2434020e..b77c1e6a 100644 --- a/web/src/pages/Setting/RateLimit/SettingsRequestRateLimit.js +++ b/web/src/pages/Setting/RateLimit/SettingsRequestRateLimit.js @@ -6,6 +6,7 @@ import { showError, showSuccess, showWarning, + verifyJSON, } from '../../../helpers'; import { useTranslation } from 'react-i18next'; @@ -18,7 +19,7 @@ export default function RequestRateLimit(props) { ModelRequestRateLimitCount: -1, ModelRequestRateLimitSuccessCount: 1000, ModelRequestRateLimitDurationMinutes: 1, - ModelRequestRateLimitGroup: '{}', + ModelRequestRateLimitGroup: '', }); const refForm = useRef(); const [inputsRow, setInputsRow] = useState(inputs); @@ -33,43 +34,32 @@ export default function RequestRateLimit(props) { } else { value = inputs[item.key]; } - if (item.key === 'ModelRequestRateLimitGroup') { - try { - JSON.parse(value); - } catch (e) { - showError(t('用户组速率限制配置不是有效的 JSON 格式!')); - return Promise.reject('Invalid JSON format'); - } - } return API.put('/api/option/', { key: item.key, value, }); }); - - const validRequests = requestQueue.filter(req => req !== null && req !== undefined && typeof req.then === 'function'); - - if (validRequests.length === 0 && requestQueue.length > 0) { - return; - } - setLoading(true); - Promise.all(validRequests) + Promise.all(requestQueue) .then((res) => { - if (validRequests.length === 1) { + if (requestQueue.length === 1) { if (res.includes(undefined)) return; - } else if (validRequests.length > 1) { + } else if (requestQueue.length > 1) { if (res.includes(undefined)) return showError(t('部分保存失败,请重试')); } + + for (let i = 0; i < res.length; i++) { + if (!res[i].data.success) { + return showError(res[i].data.message); + } + } + showSuccess(t('保存成功')); props.refresh(); - setInputsRow(structuredClone(inputs)); }) - .catch((error) => { - if (error !== 'Invalid JSON format') { - showError(t('保存失败,请重试')); - } + .catch(() => { + showError(t('保存失败,请重试')); }) .finally(() => { setLoading(false); @@ -79,15 +69,13 @@ export default function RequestRateLimit(props) { useEffect(() => { const currentInputs = {}; for (let key in props.options) { - if (Object.prototype.hasOwnProperty.call(inputs, key)) { // 使用 hasOwnProperty 检查 + if (Object.keys(inputs).includes(key)) { currentInputs[key] = props.options[key]; } } setInputs(currentInputs); setInputsRow(structuredClone(currentInputs)); - if (refForm.current) { refForm.current.setValues(currentInputs); - } }, [props.options]); return ( @@ -168,40 +156,41 @@ export default function RequestRateLimit(props) { /> - - + + verifyJSON(value), + message: t('不是合法的 JSON 字符串'), + }, + ]} extraText={
-

{t('说明:')}

+

{t('说明:')}

    -
  • {t('使用 JSON 对象格式,键为用户组名 (字符串),值为包含两个整数的数组 [总次数限制, 成功次数限制]。')}
  • -
  • {t('总次数限制: 周期内允许的总请求次数 (含失败),0 代表不限制。')}
  • -
  • {t('成功次数限制: 周期内允许的成功请求次数 (HTTP < 400),必须大于 0。')}
  • -
  • {t('此配置将优先于上方的全局限制设置。')}
  • -
  • {t('未在此处配置的用户组将使用全局限制。')}
  • +
  • {t('使用 JSON 对象格式,格式为:{"组名": [最多请求次数, 最多请求完成次数]}')}
  • +
  • {t('示例:{"default": [200, 100], "vip": [0, 1000]}。')}
  • +
  • {t('分组速率配置优先级高于全局速率限制。')}
  • {t('限制周期统一使用上方配置的“限制周期”值。')}
  • -
  • {t('输入无效的 JSON 将无法保存。')}
} - autosize={{ minRows: 5, maxRows: 15 }} - style={{ fontFamily: 'monospace' }} onChange={(value) => { - setInputs({ - ...inputs, - ModelRequestRateLimitGroup: value, - }); + setInputs({ ...inputs, ModelRequestRateLimitGroup: value }); }} />
- + @@ -211,4 +200,4 @@ export default function RequestRateLimit(props) { ); - } + } \ No newline at end of file