Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
8de14b00
authored
May 08, 2024
by
QuentinHsu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: 运营设置-通用设置
parent
4a2e9695
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
561 additions
and
390 deletions
+561
-390
web/src/components/OperationSetting.js
+338
-390
web/src/helpers/utils.js
+25
-0
web/src/pages/Setting/Operation/GeneralSettings.js
+198
-0
No files found.
web/src/components/OperationSetting.js
View file @
8de14b00
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
{
Divider
,
Form
,
Grid
,
Header
}
from
'semantic-ui-react'
;
import
{
Divider
,
Form
,
Grid
,
Header
}
from
'semantic-ui-react'
;
import
{
Card
}
from
'@douyinfe/semi-ui'
;
import
GeneralSettings
from
'../pages/Setting/Operation/GeneralSettings.js'
;
import
{
import
{
API
,
API
,
showError
,
showError
,
...
@@ -30,8 +32,8 @@ const OperationSetting = () => {
...
@@ -30,8 +32,8 @@ const OperationSetting = () => {
AutomaticEnableChannelEnabled
:
''
,
AutomaticEnableChannelEnabled
:
''
,
ChannelDisableThreshold
:
0
,
ChannelDisableThreshold
:
0
,
LogConsumeEnabled
:
''
,
LogConsumeEnabled
:
''
,
DisplayInCurrencyEnabled
:
''
,
DisplayInCurrencyEnabled
:
false
,
DisplayTokenStatEnabled
:
''
,
DisplayTokenStatEnabled
:
false
,
CheckSensitiveEnabled
:
''
,
CheckSensitiveEnabled
:
''
,
CheckSensitiveOnPromptEnabled
:
''
,
CheckSensitiveOnPromptEnabled
:
''
,
CheckSensitiveOnCompletionEnabled
:
''
,
CheckSensitiveOnCompletionEnabled
:
''
,
...
@@ -45,7 +47,7 @@ const OperationSetting = () => {
...
@@ -45,7 +47,7 @@ const OperationSetting = () => {
DataExportEnabled
:
''
,
DataExportEnabled
:
''
,
DataExportDefaultTime
:
'hour'
,
DataExportDefaultTime
:
'hour'
,
DataExportInterval
:
5
,
DataExportInterval
:
5
,
DefaultCollapseSidebar
:
''
,
// 默认折叠侧边栏
DefaultCollapseSidebar
:
false
,
// 默认折叠侧边栏
RetryTimes
:
0
,
RetryTimes
:
0
,
});
});
const
[
originInputs
,
setOriginInputs
]
=
useState
({});
const
[
originInputs
,
setOriginInputs
]
=
useState
({});
...
@@ -72,8 +74,16 @@ const OperationSetting = () => {
...
@@ -72,8 +74,16 @@ const OperationSetting = () => {
)
{
)
{
item
.
value
=
JSON
.
stringify
(
JSON
.
parse
(
item
.
value
),
null
,
2
);
item
.
value
=
JSON
.
stringify
(
JSON
.
parse
(
item
.
value
),
null
,
2
);
}
}
newInputs
[
item
.
key
]
=
item
.
value
;
if
(
item
.
key
.
endsWith
(
'Enabled'
)
||
[
'DefaultCollapseSidebar'
].
includes
(
item
.
key
)
)
{
newInputs
[
item
.
key
]
=
item
.
value
===
'true'
?
true
:
false
;
}
else
{
newInputs
[
item
.
key
]
=
item
.
value
;
}
});
});
setInputs
(
newInputs
);
setInputs
(
newInputs
);
setOriginInputs
(
newInputs
);
setOriginInputs
(
newInputs
);
}
else
{
}
else
{
...
@@ -224,396 +234,334 @@ const OperationSetting = () => {
...
@@ -224,396 +234,334 @@ const OperationSetting = () => {
showError
(
'日志清理失败:'
+
message
);
showError
(
'日志清理失败:'
+
message
);
};
};
return
(
return
(
<
Grid
columns
=
{
1
}
>
<>
<
Grid
.
Column
>
<
Card
>
<
Form
loading
=
{
loading
}
inverted
=
{
isDark
}
>
{
/* 通用设置 */
}
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
<
GeneralSettings
options
=
{
inputs
}
/
>
通用设置
<
/Card
>
<
/Header
>
<
Grid
columns
=
{
1
}
>
<
Form
.
Group
widths
=
{
4
}
>
<
Grid
.
Column
>
<
Form
.
Input
<
Form
loading
=
{
loading
}
inverted
=
{
isDark
}
>
label
=
'充值链接'
<
Divider
/>
name
=
'TopUpLink'
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
onChange
=
{
handleInputChange
}
绘图设置
autoComplete
=
'new-password'
<
/Header
>
value
=
{
inputs
.
TopUpLink
}
<
Form
.
Group
inline
>
type
=
'link'
<
Form
.
Checkbox
placeholder
=
'例如发卡网站的购买链接'
checked
=
{
inputs
.
DrawingEnabled
===
'true'
}
/>
label
=
'启用绘图功能'
<
Form
.
Input
name
=
'DrawingEnabled'
label
=
'默认聊天页面链接'
onChange
=
{
handleInputChange
}
name
=
'ChatLink'
/
>
onChange
=
{
handleInputChange
}
<
Form
.
Checkbox
autoComplete
=
'new-password'
checked
=
{
inputs
.
MjNotifyEnabled
===
'true'
}
value
=
{
inputs
.
ChatLink
}
label
=
'允许回调(会泄露服务器ip地址)'
type
=
'link'
name
=
'MjNotifyEnabled'
placeholder
=
'例如 ChatGPT Next Web 的部署地址'
onChange
=
{
handleInputChange
}
/>
/
>
<
Form
.
Input
<
Form
.
Checkbox
label
=
'聊天页面2链接'
checked
=
{
inputs
.
MjAccountFilterEnabled
===
'true'
}
name
=
'ChatLink2'
label
=
'允许AccountFilter参数'
onChange
=
{
handleInputChange
}
name
=
'MjAccountFilterEnabled'
autoComplete
=
'new-password'
onChange
=
{
handleInputChange
}
value
=
{
inputs
.
ChatLink2
}
/
>
type
=
'link'
<
Form
.
Checkbox
placeholder
=
'例如 ChatGPT Web & Midjourney 的部署地址'
checked
=
{
inputs
.
MjForwardUrlEnabled
===
'true'
}
/>
label
=
'开启之后将上游地址替换为服务器地址'
<
Form
.
Input
name
=
'MjForwardUrlEnabled'
label
=
'单位美元额度'
onChange
=
{
handleInputChange
}
name
=
'QuotaPerUnit'
/
>
onChange
=
{
handleInputChange
}
<
Form
.
Checkbox
autoComplete
=
'new-password'
checked
=
{
inputs
.
MjModeClearEnabled
===
'true'
}
value
=
{
inputs
.
QuotaPerUnit
}
label
=
'开启之后会清除用户提示词中的--fast、--relax以及--turbo参数'
type
=
'number'
name
=
'MjModeClearEnabled'
step
=
'0.01'
onChange
=
{
handleInputChange
}
placeholder
=
'一单位货币能兑换的额度'
/
>
/>
<
/Form.Group
>
<
Form
.
Input
<
Divider
/>
label
=
'失败重试次数'
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
name
=
'RetryTimes'
屏蔽词过滤设置
type
=
{
'number'
}
<
/Header
>
step
=
'1'
<
Form
.
Group
inline
>
min
=
'0'
<
Form
.
Checkbox
onChange
=
{
handleInputChange
}
checked
=
{
inputs
.
CheckSensitiveEnabled
===
'true'
}
autoComplete
=
'new-password'
label
=
'启用屏蔽词过滤功能'
value
=
{
inputs
.
RetryTimes
}
name
=
'CheckSensitiveEnabled'
placeholder
=
'失败重试次数'
onChange
=
{
handleInputChange
}
/>
/
>
<
/Form.Group
>
<
/Form.Group
>
<
Form
.
Group
inline
>
<
Form
.
Group
inline
>
<
Form
.
Checkbox
<
Form
.
Checkbox
checked
=
{
inputs
.
DisplayInCurrencyEnabled
===
'true'
}
checked
=
{
inputs
.
CheckSensitiveOnPromptEnabled
===
'true'
}
label
=
'以货币形式显示额度'
label
=
'启用prompt检查'
name
=
'DisplayInCurrencyEnabled'
name
=
'CheckSensitiveOnPromptEnabled'
onChange
=
{
handleInputChange
}
onChange
=
{
handleInputChange
}
/
>
/
>
<
Form
.
Checkbox
{
/*<Form.Checkbox*/
}
checked
=
{
inputs
.
DisplayTokenStatEnabled
===
'true'
}
{
/* checked={inputs.CheckSensitiveOnCompletionEnabled === 'true'}*/
}
label
=
'Billing 相关 API 显示令牌额度而非用户额度'
{
/* label='启用生成内容检查'*/
}
name
=
'DisplayTokenStatEnabled'
{
/* name='CheckSensitiveOnCompletionEnabled'*/
}
onChange
=
{
handleInputChange
}
{
/* onChange={handleInputChange}*/
}
/
>
{
/*/>*/
}
<
Form
.
Checkbox
<
/Form.Group
>
checked
=
{
inputs
.
DefaultCollapseSidebar
===
'true'
}
{
/*<Form.Group inline>*/
}
label
=
'默认折叠侧边栏'
{
/* <Form.Checkbox*/
}
name
=
'DefaultCollapseSidebar'
{
/* checked={inputs.StopOnSensitiveEnabled === 'true'}*/
}
onChange
=
{
handleInputChange
}
{
/* label='在检测到屏蔽词时,立刻停止生成,否则替换屏蔽词'*/
}
/
>
{
/* name='StopOnSensitiveEnabled'*/
}
<
/Form.Group
>
{
/* onChange={handleInputChange}*/
}
<
Form
.
Button
{
/* />*/
}
onClick
=
{()
=>
{
{
/*</Form.Group>*/
}
submitConfig
(
'general'
).
then
();
{
/*<Form.Group>*/
}
}}
{
/* <Form.Input*/
}
>
{
/* label="流模式下缓存队列,默认不缓存,设置越大检测越准确,但是回复会有卡顿感"*/
}
保存通用设置
{
/* name="StreamCacheTextLength"*/
}
<
/Form.Button
>
{
/* onChange={handleInputChange}*/
}
<
Divider
/>
{
/* value={inputs.StreamCacheQueueLength}*/
}
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
{
/* type="number"*/
}
绘图设置
{
/* min="0"*/
}
<
/Header
>
{
/* placeholder="例如:10"*/
}
<
Form
.
Group
inline
>
{
/* />*/
}
<
Form
.
Checkbox
{
/*</Form.Group>*/
}
checked
=
{
inputs
.
DrawingEnabled
===
'true'
}
<
Form
.
Group
widths
=
'equal'
>
label
=
'启用绘图功能'
<
Form
.
TextArea
name
=
'DrawingEnabled'
label
=
'屏蔽词列表,一行一个屏蔽词,不需要符号分割'
onChange
=
{
handleInputChange
}
name
=
'SensitiveWords'
/
>
onChange
=
{
handleInputChange
}
<
Form
.
Checkbox
style
=
{{
checked
=
{
inputs
.
MjNotifyEnabled
===
'true'
}
minHeight
:
250
,
label
=
'允许回调(会泄露服务器ip地址)'
fontFamily
:
'JetBrains Mono, Consolas'
,
name
=
'MjNotifyEnabled'
}}
onChange
=
{
handleInputChange
}
value
=
{
inputs
.
SensitiveWords
}
/
>
placeholder
=
'一行一个屏蔽词'
<
Form
.
Checkbox
/>
checked
=
{
inputs
.
MjAccountFilterEnabled
===
'true'
}
<
/Form.Group
>
label
=
'允许AccountFilter参数'
<
Form
.
Button
name
=
'MjAccountFilterEnabled'
onClick
=
{()
=>
{
onChange
=
{
handleInputChange
}
submitConfig
(
'words'
).
then
();
/
>
<
Form
.
Checkbox
checked
=
{
inputs
.
MjForwardUrlEnabled
===
'true'
}
label
=
'开启之后将上游地址替换为服务器地址'
name
=
'MjForwardUrlEnabled'
onChange
=
{
handleInputChange
}
/
>
<
Form
.
Checkbox
checked
=
{
inputs
.
MjModeClearEnabled
===
'true'
}
label
=
'开启之后会清除用户提示词中的--fast、--relax以及--turbo参数'
name
=
'MjModeClearEnabled'
onChange
=
{
handleInputChange
}
/
>
<
/Form.Group
>
<
Divider
/>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
屏蔽词过滤设置
<
/Header
>
<
Form
.
Group
inline
>
<
Form
.
Checkbox
checked
=
{
inputs
.
CheckSensitiveEnabled
===
'true'
}
label
=
'启用屏蔽词过滤功能'
name
=
'CheckSensitiveEnabled'
onChange
=
{
handleInputChange
}
/
>
<
/Form.Group
>
<
Form
.
Group
inline
>
<
Form
.
Checkbox
checked
=
{
inputs
.
CheckSensitiveOnPromptEnabled
===
'true'
}
label
=
'启用prompt检查'
name
=
'CheckSensitiveOnPromptEnabled'
onChange
=
{
handleInputChange
}
/
>
{
/*<Form.Checkbox*/
}
{
/* checked={inputs.CheckSensitiveOnCompletionEnabled === 'true'}*/
}
{
/* label='启用生成内容检查'*/
}
{
/* name='CheckSensitiveOnCompletionEnabled'*/
}
{
/* onChange={handleInputChange}*/
}
{
/*/>*/
}
<
/Form.Group
>
{
/*<Form.Group inline>*/
}
{
/* <Form.Checkbox*/
}
{
/* checked={inputs.StopOnSensitiveEnabled === 'true'}*/
}
{
/* label='在检测到屏蔽词时,立刻停止生成,否则替换屏蔽词'*/
}
{
/* name='StopOnSensitiveEnabled'*/
}
{
/* onChange={handleInputChange}*/
}
{
/* />*/
}
{
/*</Form.Group>*/
}
{
/*<Form.Group>*/
}
{
/* <Form.Input*/
}
{
/* label="流模式下缓存队列,默认不缓存,设置越大检测越准确,但是回复会有卡顿感"*/
}
{
/* name="StreamCacheTextLength"*/
}
{
/* onChange={handleInputChange}*/
}
{
/* value={inputs.StreamCacheQueueLength}*/
}
{
/* type="number"*/
}
{
/* min="0"*/
}
{
/* placeholder="例如:10"*/
}
{
/* />*/
}
{
/*</Form.Group>*/
}
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
TextArea
label
=
'屏蔽词列表,一行一个屏蔽词,不需要符号分割'
name
=
'SensitiveWords'
onChange
=
{
handleInputChange
}
style
=
{{
minHeight
:
250
,
fontFamily
:
'JetBrains Mono, Consolas'
}}
value
=
{
inputs
.
SensitiveWords
}
placeholder
=
'一行一个屏蔽词'
/>
<
/Form.Group
>
<
Form
.
Button
onClick
=
{()
=>
{
submitConfig
(
'words'
).
then
();
}}
>
保存屏蔽词设置
<
/Form.Button
>
<
Divider
/>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
日志设置
<
/Header
>
<
Form
.
Group
inline
>
<
Form
.
Checkbox
checked
=
{
inputs
.
LogConsumeEnabled
===
'true'
}
label
=
'启用额度消费日志记录'
name
=
'LogConsumeEnabled'
onChange
=
{
handleInputChange
}
/
>
<
/Form.Group
>
<
Form
.
Group
widths
=
{
4
}
>
<
Form
.
Input
label
=
'目标时间'
value
=
{
historyTimestamp
}
type
=
'datetime-local'
name
=
'history_timestamp'
onChange
=
{(
e
,
{
name
,
value
})
=>
{
setHistoryTimestamp
(
value
);
}}
}}
/
>
>
<
/Form.Group
>
保存屏蔽词设置
<
Form
.
Button
<
/Form.Button
>
onClick
=
{()
=>
{
<
Divider
/>
deleteHistoryLogs
().
then
();
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
}}
日志设置
>
<
/Header
>
清理历史日志
<
Form
.
Group
inline
>
<
/Form.Button
>
<
Form
.
Checkbox
<
Divider
/>
checked
=
{
inputs
.
LogConsumeEnabled
===
'true'
}
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
label
=
'启用额度消费日志记录'
数据看板
name
=
'LogConsumeEnabled'
<
/Header
>
onChange
=
{
handleInputChange
}
<
Form
.
Checkbox
/
>
checked
=
{
inputs
.
DataExportEnabled
===
'true'
}
<
/Form.Group
>
label
=
'启用数据看板(实验性)'
<
Form
.
Group
widths
=
{
4
}
>
name
=
'DataExportEnabled'
<
Form
.
Input
onChange
=
{
handleInputChange
}
label
=
'目标时间'
/
>
value
=
{
historyTimestamp
}
<
Form
.
Group
>
type
=
'datetime-local'
<
Form
.
Input
name
=
'history_timestamp'
label
=
'数据看板更新间隔(分钟,设置过短会影响数据库性能)'
onChange
=
{(
e
,
{
name
,
value
})
=>
{
name
=
'DataExportInterval'
setHistoryTimestamp
(
value
);
type
=
{
'number'
}
}}
step
=
'1'
/
>
min
=
'1'
<
/Form.Group
>
onChange
=
{
handleInputChange
}
<
Form
.
Button
autoComplete
=
'new-password'
onClick
=
{()
=>
{
value
=
{
inputs
.
DataExportInterval
}
deleteHistoryLogs
().
then
();
placeholder
=
'数据看板更新间隔(分钟,设置过短会影响数据库性能)'
}}
/>
>
<
Form
.
Select
清理历史日志
label
=
'数据看板默认时间粒度(仅修改展示粒度,统计精确到小时)'
<
/Form.Button
>
options
=
{
timeOptions
}
<
Divider
/>
name
=
'DataExportDefaultTime'
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
onChange
=
{
handleInputChange
}
数据看板
autoComplete
=
'new-password'
<
/Header
>
value
=
{
inputs
.
DataExportDefaultTime
}
placeholder
=
'数据看板默认时间粒度'
/>
<
/Form.Group
>
<
Divider
/>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
监控设置
<
/Header
>
<
Form
.
Group
widths
=
{
3
}
>
<
Form
.
Input
label
=
'最长响应时间'
name
=
'ChannelDisableThreshold'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
ChannelDisableThreshold
}
type
=
'number'
min
=
'0'
placeholder
=
'单位秒,当运行通道全部测试时,超过此时间将自动禁用通道'
/>
<
Form
.
Input
label
=
'额度提醒阈值'
name
=
'QuotaRemindThreshold'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaRemindThreshold
}
type
=
'number'
min
=
'0'
placeholder
=
'低于此额度时将发送邮件提醒用户'
/>
<
/Form.Group
>
<
Form
.
Group
inline
>
<
Form
.
Checkbox
checked
=
{
inputs
.
AutomaticDisableChannelEnabled
===
'true'
}
label
=
'失败时自动禁用通道'
name
=
'AutomaticDisableChannelEnabled'
onChange
=
{
handleInputChange
}
/
>
<
Form
.
Checkbox
<
Form
.
Checkbox
checked
=
{
inputs
.
AutomaticEnableChannelEnabled
===
'true'
}
checked
=
{
inputs
.
DataExportEnabled
===
'true'
}
label
=
'成功时自动启用通道'
label
=
'启用数据看板(实验性)'
name
=
'AutomaticEnableChannelEnabled'
name
=
'DataExportEnabled'
onChange
=
{
handleInputChange
}
/
>
<
/Form.Group
>
<
Form
.
Button
onClick
=
{()
=>
{
submitConfig
(
'monitor'
).
then
();
}}
>
保存监控设置
<
/Form.Button
>
<
Divider
/>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
额度设置
<
/Header
>
<
Form
.
Group
widths
=
{
4
}
>
<
Form
.
Input
label
=
'新用户初始额度'
name
=
'QuotaForNewUser'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaForNewUser
}
type
=
'number'
min
=
'0'
placeholder
=
'例如:100'
/>
<
Form
.
Input
label
=
'请求预扣费额度'
name
=
'PreConsumedQuota'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
PreConsumedQuota
}
type
=
'number'
min
=
'0'
placeholder
=
'请求结束后多退少补'
/>
<
Form
.
Input
label
=
'邀请新用户奖励额度'
name
=
'QuotaForInviter'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaForInviter
}
type
=
'number'
min
=
'0'
placeholder
=
'例如:2000'
/>
<
Form
.
Input
label
=
'新用户使用邀请码奖励额度'
name
=
'QuotaForInvitee'
onChange
=
{
handleInputChange
}
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaForInvitee
}
type
=
'number'
min
=
'0'
placeholder
=
'例如:1000'
/
>
/
>
<
/Form.Group
>
<
Form
.
Group
>
<
Form
.
Button
<
Form
.
Input
onClick
=
{()
=>
{
label
=
'数据看板更新间隔(分钟,设置过短会影响数据库性能)'
submitConfig
(
'quota'
).
then
();
name
=
'DataExportInterval'
}}
type
=
{
'number'
}
>
step
=
'1'
保存额度设置
min
=
'1'
<
/Form.Button
>
onChange
=
{
handleInputChange
}
<
Divider
/>
autoComplete
=
'new-password'
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
value
=
{
inputs
.
DataExportInterval
}
倍率设置
placeholder
=
'数据看板更新间隔(分钟,设置过短会影响数据库性能)'
<
/Header
>
/>
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
Select
<
Form
.
TextArea
label
=
'数据看板默认时间粒度(仅修改展示粒度,统计精确到小时)'
label
=
'模型固定价格(一次调用消耗多少刀,优先级大于模型倍率)'
options
=
{
timeOptions
}
name
=
'ModelPrice'
name
=
'DataExportDefaultTime'
onChange
=
{
handleInputChange
}
onChange
=
{
handleInputChange
}
style
=
{{
minHeight
:
250
,
fontFamily
:
'JetBrains Mono, Consolas'
}}
autoComplete
=
'new-password'
autoComplete
=
'new-password'
value
=
{
inputs
.
DataExportDefaultTime
}
value
=
{
inputs
.
ModelPrice
}
placeholder
=
'数据看板默认时间粒度'
placeholder
=
'为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 "gpt-4-gizmo-*": 0.1,一次消耗0.1刀'
/>
/>
<
/Form.Group
>
<
/Form.Group
>
<
Divider
/>
<
Form
.
Group
widths
=
'equal'
>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
<
Form
.
TextArea
监控设置
label
=
'模型倍率'
<
/Header
>
name
=
'ModelRatio'
<
Form
.
Group
widths
=
{
3
}
>
onChange
=
{
handleInputChange
}
<
Form
.
Input
style
=
{{
minHeight
:
250
,
fontFamily
:
'JetBrains Mono, Consolas'
}}
label
=
'最长响应时间'
autoComplete
=
'new-password'
name
=
'ChannelDisableThreshold'
value
=
{
inputs
.
ModelRatio
}
onChange
=
{
handleInputChange
}
placeholder
=
'为一个 JSON 文本,键为模型名称,值为倍率'
autoComplete
=
'new-password'
/>
value
=
{
inputs
.
ChannelDisableThreshold
}
<
/Form.Group
>
type
=
'number'
<
Form
.
Group
widths
=
'equal'
>
min
=
'0'
<
Form
.
TextArea
placeholder
=
'单位秒,当运行通道全部测试时,超过此时间将自动禁用通道'
label
=
'分组倍率'
/>
name
=
'GroupRatio'
<
Form
.
Input
onChange
=
{
handleInputChange
}
label
=
'额度提醒阈值'
style
=
{{
minHeight
:
250
,
fontFamily
:
'JetBrains Mono, Consolas'
}}
name
=
'QuotaRemindThreshold'
autoComplete
=
'new-password'
onChange
=
{
handleInputChange
}
value
=
{
inputs
.
GroupRatio
}
autoComplete
=
'new-password'
placeholder
=
'为一个 JSON 文本,键为分组名称,值为倍率'
value
=
{
inputs
.
QuotaRemindThreshold
}
/>
type
=
'number'
<
/Form.Group
>
min
=
'0'
<
Form
.
Button
placeholder
=
'低于此额度时将发送邮件提醒用户'
onClick
=
{()
=>
{
/>
submitConfig
(
'ratio'
).
then
();
<
/Form.Group
>
}}
<
Form
.
Group
inline
>
>
<
Form
.
Checkbox
保存倍率设置
checked
=
{
inputs
.
AutomaticDisableChannelEnabled
===
'true'
}
<
/Form.Button
>
label
=
'失败时自动禁用通道'
<
/Form
>
name
=
'AutomaticDisableChannelEnabled'
<
/Grid.Column
>
onChange
=
{
handleInputChange
}
<
/Grid
>
/
>
<
Form
.
Checkbox
checked
=
{
inputs
.
AutomaticEnableChannelEnabled
===
'true'
}
label
=
'成功时自动启用通道'
name
=
'AutomaticEnableChannelEnabled'
onChange
=
{
handleInputChange
}
/
>
<
/Form.Group
>
<
Form
.
Button
onClick
=
{()
=>
{
submitConfig
(
'monitor'
).
then
();
}}
>
保存监控设置
<
/Form.Button
>
<
Divider
/>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
额度设置
<
/Header
>
<
Form
.
Group
widths
=
{
4
}
>
<
Form
.
Input
label
=
'新用户初始额度'
name
=
'QuotaForNewUser'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaForNewUser
}
type
=
'number'
min
=
'0'
placeholder
=
'例如:100'
/>
<
Form
.
Input
label
=
'请求预扣费额度'
name
=
'PreConsumedQuota'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
PreConsumedQuota
}
type
=
'number'
min
=
'0'
placeholder
=
'请求结束后多退少补'
/>
<
Form
.
Input
label
=
'邀请新用户奖励额度'
name
=
'QuotaForInviter'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaForInviter
}
type
=
'number'
min
=
'0'
placeholder
=
'例如:2000'
/>
<
Form
.
Input
label
=
'新用户使用邀请码奖励额度'
name
=
'QuotaForInvitee'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaForInvitee
}
type
=
'number'
min
=
'0'
placeholder
=
'例如:1000'
/>
<
/Form.Group
>
<
Form
.
Button
onClick
=
{()
=>
{
submitConfig
(
'quota'
).
then
();
}}
>
保存额度设置
<
/Form.Button
>
<
Divider
/>
<
Header
as
=
'h3'
inverted
=
{
isDark
}
>
倍率设置
<
/Header
>
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
TextArea
label
=
'模型固定价格(一次调用消耗多少刀,优先级大于模型倍率)'
name
=
'ModelPrice'
onChange
=
{
handleInputChange
}
style
=
{{
minHeight
:
250
,
fontFamily
:
'JetBrains Mono, Consolas'
,
}}
autoComplete
=
'new-password'
value
=
{
inputs
.
ModelPrice
}
placeholder
=
'为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 "gpt-4-gizmo-*": 0.1,一次消耗0.1刀'
/>
<
/Form.Group
>
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
TextArea
label
=
'模型倍率'
name
=
'ModelRatio'
onChange
=
{
handleInputChange
}
style
=
{{
minHeight
:
250
,
fontFamily
:
'JetBrains Mono, Consolas'
,
}}
autoComplete
=
'new-password'
value
=
{
inputs
.
ModelRatio
}
placeholder
=
'为一个 JSON 文本,键为模型名称,值为倍率'
/>
<
/Form.Group
>
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
TextArea
label
=
'分组倍率'
name
=
'GroupRatio'
onChange
=
{
handleInputChange
}
style
=
{{
minHeight
:
250
,
fontFamily
:
'JetBrains Mono, Consolas'
,
}}
autoComplete
=
'new-password'
value
=
{
inputs
.
GroupRatio
}
placeholder
=
'为一个 JSON 文本,键为分组名称,值为倍率'
/>
<
/Form.Group
>
<
Form
.
Button
onClick
=
{()
=>
{
submitConfig
(
'ratio'
).
then
();
}}
>
保存倍率设置
<
/Form.Button
>
<
/Form
>
<
/Grid.Column
>
<
/Grid
>
<
/
>
);
);
};
};
...
...
web/src/helpers/utils.js
View file @
8de14b00
...
@@ -220,3 +220,28 @@ export function shouldShowPrompt(id) {
...
@@ -220,3 +220,28 @@ export function shouldShowPrompt(id) {
export
function
setPromptShown
(
id
)
{
export
function
setPromptShown
(
id
)
{
localStorage
.
setItem
(
`prompt-
${
id
}
`
,
'true'
);
localStorage
.
setItem
(
`prompt-
${
id
}
`
,
'true'
);
}
}
/**
* 比较两个对象的属性,找出有变化的属性,并返回包含变化属性信息的数组
* @param {Object} oldObject - 旧对象
* @param {Object} newObject - 新对象
* @return {Array} 包含变化属性信息的数组,每个元素是一个对象,包含 key, oldValue 和 newValue
*/
export
function
compareObjects
(
oldObject
,
newObject
)
{
const
changedProperties
=
[];
// 比较两个对象的属性
for
(
const
key
in
oldObject
)
{
if
(
oldObject
.
hasOwnProperty
(
key
)
&&
newObject
.
hasOwnProperty
(
key
))
{
if
(
oldObject
[
key
]
!==
newObject
[
key
])
{
changedProperties
.
push
({
key
:
key
,
oldValue
:
oldObject
[
key
],
newValue
:
newObject
[
key
],
});
}
}
}
return
changedProperties
;
}
web/src/pages/Setting/Operation/GeneralSettings.js
0 → 100644
View file @
8de14b00
import
React
,
{
useEffect
,
useState
,
useRef
}
from
'react'
;
import
{
Button
,
Col
,
Form
,
Row
,
Spin
}
from
'@douyinfe/semi-ui'
;
import
{
compareObjects
,
API
,
showError
,
showSuccess
,
showWarning
,
}
from
'../../../helpers'
;
export
default
function
GeneralSettings
(
props
)
{
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
inputs
,
setInputs
]
=
useState
({
TopUpLink
:
''
,
ChatLink
:
''
,
ChatLink2
:
''
,
QuotaPerUnit
:
''
,
RetryTimes
:
''
,
DisplayInCurrencyEnabled
:
false
,
DisplayTokenStatEnabled
:
false
,
DefaultCollapseSidebar
:
false
,
});
const
refForm
=
useRef
();
const
[
inputsRow
,
setInputsRow
]
=
useState
(
inputs
);
function
onChange
(
value
,
e
)
{
const
name
=
e
.
target
.
id
;
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
}
function
onSubmit
()
{
const
updateArray
=
compareObjects
(
inputs
,
inputsRow
);
if
(
!
updateArray
.
length
)
return
showWarning
(
'你似乎并没有修改什么'
);
const
requestQueue
=
updateArray
.
map
((
item
)
=>
{
let
value
=
''
;
if
(
typeof
inputs
[
item
.
key
]
===
'boolean'
)
{
value
=
String
(
inputs
[
item
.
key
]);
}
else
{
value
=
inputs
[
item
.
key
];
}
return
API
.
put
(
'/api/option/'
,
{
key
:
item
.
key
,
value
,
});
});
setLoading
(
true
);
Promise
.
all
(
requestQueue
)
.
then
((
res
)
=>
{
if
(
requestQueue
.
length
===
1
)
{
if
(
res
.
includes
(
undefined
))
return
;
}
else
if
(
requestQueue
.
length
>
1
)
{
if
(
res
.
includes
(
undefined
))
return
showError
(
'部分更新失败'
);
}
showSuccess
(
'更新成功'
);
})
.
catch
(()
=>
{
showError
(
'更新失败'
);
})
.
finally
(()
=>
{
setLoading
(
false
);
setInputsRow
(
structuredClone
(
inputs
));
});
}
useEffect
(()
=>
{
const
currentInputs
=
{};
for
(
let
key
in
props
.
options
)
{
if
(
Object
.
keys
(
inputs
).
includes
(
key
))
{
currentInputs
[
key
]
=
props
.
options
[
key
];
}
}
setInputs
(
currentInputs
);
setInputsRow
(
structuredClone
(
currentInputs
));
refForm
.
current
.
setValues
(
currentInputs
);
},
[
props
.
options
]);
return
(
<>
<
Spin
spinning
=
{
loading
}
>
<
Form
values
=
{
inputs
}
getFormApi
=
{(
formAPI
)
=>
(
refForm
.
current
=
formAPI
)}
style
=
{{
marginBottom
:
15
}}
>
<
Form
.
Section
text
=
{
'通用设置'
}
>
<
Row
gutter
=
{
16
}
>
<
Col
span
=
{
8
}
>
<
Form
.
Input
field
=
{
'TopUpLink'
}
label
=
{
'充值链接'
}
initValue
=
{
''
}
placeholder
=
{
'例如发卡网站的购买链接'
}
onChange
=
{
onChange
}
showClear
/>
<
/Col
>
<
Col
span
=
{
8
}
>
<
Form
.
Input
field
=
{
'ChatLink'
}
label
=
{
'默认聊天页面链接'
}
initValue
=
{
''
}
placeholder
=
'例如 ChatGPT Next Web 的部署地址'
onChange
=
{
onChange
}
showClear
/>
<
/Col
>
<
Col
span
=
{
8
}
>
<
Form
.
Input
field
=
{
'ChatLink2'
}
label
=
{
'聊天页面 2 链接'
}
initValue
=
{
''
}
placeholder
=
'例如 ChatGPT Next Web 的部署地址'
onChange
=
{
onChange
}
showClear
/>
<
/Col
>
<
Col
span
=
{
8
}
>
<
Form
.
Input
field
=
{
'QuotaPerUnit'
}
label
=
{
'单位美元额度'
}
initValue
=
{
''
}
placeholder
=
'一单位货币能兑换的额度'
onChange
=
{
onChange
}
showClear
/>
<
/Col
>
<
Col
span
=
{
8
}
>
<
Form
.
Input
field
=
{
'RetryTimes'
}
label
=
{
'失败重试次数'
}
initValue
=
{
''
}
placeholder
=
'失败重试次数'
onChange
=
{
onChange
}
showClear
/>
<
/Col
>
<
/Row
>
<
Row
gutter
=
{
16
}
>
<
Col
span
=
{
8
}
>
<
Form
.
Switch
field
=
{
'DisplayInCurrencyEnabled'
}
label
=
{
'以货币形式显示额度'
}
size
=
'large'
checkedText
=
'|'
uncheckedText
=
'〇'
defaultChecked
=
{
false
}
checked
=
{
false
}
onChange
=
{(
value
)
=>
{
setInputs
({
...
inputs
,
DisplayInCurrencyEnabled
:
value
,
});
}}
/
>
<
/Col
>
<
Col
span
=
{
8
}
>
<
Form
.
Switch
field
=
{
'DisplayTokenStatEnabled'
}
label
=
{
'Billing 相关 API 显示令牌额度而非用户额度'
}
size
=
'large'
checkedText
=
'|'
uncheckedText
=
'〇'
defaultChecked
=
{
false
}
checked
=
{
false
}
onChange
=
{(
value
)
=>
setInputs
({
...
inputs
,
DisplayTokenStatEnabled
:
value
,
})
}
/
>
<
/Col
>
<
Col
span
=
{
8
}
>
<
Form
.
Switch
field
=
{
'DefaultCollapseSidebar'
}
label
=
{
'默认折叠侧边栏'
}
size
=
'large'
checkedText
=
'|'
uncheckedText
=
'〇'
defaultChecked
=
{
false
}
checked
=
{
false
}
onChange
=
{(
value
)
=>
setInputs
({
...
inputs
,
DefaultCollapseSidebar
:
value
,
})
}
/
>
<
/Col
>
<
/Row
>
<
Row
>
<
Button
size
=
'large'
onClick
=
{
onSubmit
}
>
保存通用设置
<
/Button
>
<
/Row
>
<
/Form.Section
>
<
/Form
>
<
/Spin
>
<
/
>
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment