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
946aa773
authored
Jun 19, 2023
by
JustSong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: make operation settings separated from system settings
parent
5fc5cdf2
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
292 additions
and
191 deletions
+292
-191
web/src/components/OperationSetting.js
+282
-0
web/src/components/SystemSetting.js
+1
-191
web/src/pages/Setting/index.js
+9
-0
No files found.
web/src/components/OperationSetting.js
0 → 100644
View file @
946aa773
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
{
Divider
,
Form
,
Grid
,
Header
}
from
'semantic-ui-react'
;
import
{
API
,
showError
,
verifyJSON
}
from
'../helpers'
;
const
OperationSetting
=
()
=>
{
let
[
inputs
,
setInputs
]
=
useState
({
QuotaForNewUser
:
0
,
QuotaForInviter
:
0
,
QuotaForInvitee
:
0
,
QuotaRemindThreshold
:
0
,
PreConsumedQuota
:
0
,
ModelRatio
:
''
,
GroupRatio
:
''
,
TopUpLink
:
''
,
ChatLink
:
''
,
AutomaticDisableChannelEnabled
:
''
,
ChannelDisableThreshold
:
0
,
LogConsumeEnabled
:
''
});
const
[
originInputs
,
setOriginInputs
]
=
useState
({});
let
[
loading
,
setLoading
]
=
useState
(
false
);
const
getOptions
=
async
()
=>
{
const
res
=
await
API
.
get
(
'/api/option/'
);
const
{
success
,
message
,
data
}
=
res
.
data
;
if
(
success
)
{
let
newInputs
=
{};
data
.
forEach
((
item
)
=>
{
if
(
item
.
key
===
'ModelRatio'
||
item
.
key
===
'GroupRatio'
)
{
item
.
value
=
JSON
.
stringify
(
JSON
.
parse
(
item
.
value
),
null
,
2
);
}
newInputs
[
item
.
key
]
=
item
.
value
;
});
setInputs
(
newInputs
);
setOriginInputs
(
newInputs
);
}
else
{
showError
(
message
);
}
};
useEffect
(()
=>
{
getOptions
().
then
();
},
[]);
const
updateOption
=
async
(
key
,
value
)
=>
{
setLoading
(
true
);
if
(
key
.
endsWith
(
'Enabled'
))
{
value
=
inputs
[
key
]
===
'true'
?
'false'
:
'true'
;
}
const
res
=
await
API
.
put
(
'/api/option/'
,
{
key
,
value
});
const
{
success
,
message
}
=
res
.
data
;
if
(
success
)
{
setInputs
((
inputs
)
=>
({
...
inputs
,
[
key
]:
value
}));
}
else
{
showError
(
message
);
}
setLoading
(
false
);
};
const
handleInputChange
=
async
(
e
,
{
name
,
value
})
=>
{
if
(
name
.
endsWith
(
'Enabled'
))
{
await
updateOption
(
name
,
value
);
}
else
{
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
}
};
const
submitConfig
=
async
(
group
)
=>
{
switch
(
group
)
{
case
'monitor'
:
if
(
originInputs
[
'AutomaticDisableChannelEnabled'
]
!==
inputs
.
AutomaticDisableChannelEnabled
)
{
await
updateOption
(
'AutomaticDisableChannelEnabled'
,
inputs
.
AutomaticDisableChannelEnabled
);
}
if
(
originInputs
[
'ChannelDisableThreshold'
]
!==
inputs
.
ChannelDisableThreshold
)
{
await
updateOption
(
'ChannelDisableThreshold'
,
inputs
.
ChannelDisableThreshold
);
}
if
(
originInputs
[
'QuotaRemindThreshold'
]
!==
inputs
.
QuotaRemindThreshold
)
{
await
updateOption
(
'QuotaRemindThreshold'
,
inputs
.
QuotaRemindThreshold
);
}
break
;
case
'ratio'
:
if
(
originInputs
[
'ModelRatio'
]
!==
inputs
.
ModelRatio
)
{
if
(
!
verifyJSON
(
inputs
.
ModelRatio
))
{
showError
(
'模型倍率不是合法的 JSON 字符串'
);
return
;
}
await
updateOption
(
'ModelRatio'
,
inputs
.
ModelRatio
);
}
if
(
originInputs
[
'GroupRatio'
]
!==
inputs
.
GroupRatio
)
{
if
(
!
verifyJSON
(
inputs
.
GroupRatio
))
{
showError
(
'分组倍率不是合法的 JSON 字符串'
);
return
;
}
await
updateOption
(
'GroupRatio'
,
inputs
.
GroupRatio
);
}
break
;
case
'quota'
:
if
(
originInputs
[
'QuotaForNewUser'
]
!==
inputs
.
QuotaForNewUser
)
{
await
updateOption
(
'QuotaForNewUser'
,
inputs
.
QuotaForNewUser
);
}
if
(
originInputs
[
'QuotaForInvitee'
]
!==
inputs
.
QuotaForInvitee
)
{
await
updateOption
(
'QuotaForInvitee'
,
inputs
.
QuotaForInvitee
);
}
if
(
originInputs
[
'QuotaForInviter'
]
!==
inputs
.
QuotaForInviter
)
{
await
updateOption
(
'QuotaForInviter'
,
inputs
.
QuotaForInviter
);
}
if
(
originInputs
[
'PreConsumedQuota'
]
!==
inputs
.
PreConsumedQuota
)
{
await
updateOption
(
'PreConsumedQuota'
,
inputs
.
PreConsumedQuota
);
}
break
;
case
'general'
:
if
(
originInputs
[
'TopUpLink'
]
!==
inputs
.
TopUpLink
)
{
await
updateOption
(
'TopUpLink'
,
inputs
.
TopUpLink
);
}
if
(
originInputs
[
'ChatLink'
]
!==
inputs
.
ChatLink
)
{
await
updateOption
(
'ChatLink'
,
inputs
.
ChatLink
);
}
break
;
}
};
return
(
<
Grid
columns
=
{
1
}
>
<
Grid
.
Column
>
<
Form
loading
=
{
loading
}
>
<
Header
as
=
'h3'
>
通用设置
<
/Header
>
<
Form
.
Group
widths
=
{
2
}
>
<
Form
.
Input
label
=
'充值链接'
name
=
'TopUpLink'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
TopUpLink
}
type
=
'link'
placeholder
=
'例如发卡网站的购买链接'
/>
<
Form
.
Input
label
=
'聊天页面链接'
name
=
'ChatLink'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
ChatLink
}
type
=
'link'
placeholder
=
'例如 ChatGPT Next Web 的部署地址'
/>
<
/Form.Group
>
<
Form
.
Button
onClick
=
{()
=>
{
submitConfig
(
'general'
).
then
();
}}
>
保存通用设置
<
/Form.Button
>
<
Divider
/>
<
Header
as
=
'h3'
>
监控设置
<
/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.Group
>
<
Form
.
Button
onClick
=
{()
=>
{
submitConfig
(
'monitor'
).
then
();
}}
>
保存监控设置
<
/Form.Button
>
<
Divider
/>
<
Header
as
=
'h3'
>
额度设置
<
/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'
>
倍率设置
<
/Header
>
<
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
.
Checkbox
checked
=
{
inputs
.
LogConsumeEnabled
===
'true'
}
label
=
'启用额度消费日志记录'
name
=
'LogConsumeEnabled'
onChange
=
{
handleInputChange
}
/
>
<
Form
.
Button
onClick
=
{()
=>
{
submitConfig
(
'ratio'
).
then
();
}}
>
保存倍率设置
<
/Form.Button
>
<
/Form
>
<
/Grid.Column
>
<
/Grid
>
);
};
export
default
OperationSetting
;
web/src/components/SystemSetting.js
View file @
946aa773
...
...
@@ -26,18 +26,6 @@ const SystemSetting = () => {
TurnstileSiteKey
:
''
,
TurnstileSecretKey
:
''
,
RegisterEnabled
:
''
,
QuotaForNewUser
:
0
,
QuotaForInviter
:
0
,
QuotaForInvitee
:
0
,
QuotaRemindThreshold
:
0
,
PreConsumedQuota
:
0
,
ModelRatio
:
''
,
GroupRatio
:
''
,
TopUpLink
:
''
,
ChatLink
:
''
,
AutomaticDisableChannelEnabled
:
''
,
ChannelDisableThreshold
:
0
,
LogConsumeEnabled
:
''
});
const
[
originInputs
,
setOriginInputs
]
=
useState
({});
let
[
loading
,
setLoading
]
=
useState
(
false
);
...
...
@@ -71,8 +59,6 @@ const SystemSetting = () => {
case
'WeChatAuthEnabled'
:
case
'TurnstileCheckEnabled'
:
case
'RegisterEnabled'
:
case
'AutomaticDisableChannelEnabled'
:
case
'LogConsumeEnabled'
:
value
=
inputs
[
key
]
===
'true'
?
'false'
:
'true'
;
break
;
default
:
...
...
@@ -102,16 +88,7 @@ const SystemSetting = () => {
name
===
'WeChatServerToken'
||
name
===
'WeChatAccountQRCodeImageURL'
||
name
===
'TurnstileSiteKey'
||
name
===
'TurnstileSecretKey'
||
name
===
'QuotaForNewUser'
||
name
===
'QuotaForInviter'
||
name
===
'QuotaForInvitee'
||
name
===
'QuotaRemindThreshold'
||
name
===
'PreConsumedQuota'
||
name
===
'ModelRatio'
||
name
===
'GroupRatio'
||
name
===
'TopUpLink'
||
name
===
'ChatLink'
name
===
'TurnstileSecretKey'
)
{
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
}
else
{
...
...
@@ -124,44 +101,6 @@ const SystemSetting = () => {
await
updateOption
(
'ServerAddress'
,
ServerAddress
);
};
const
submitOperationConfig
=
async
()
=>
{
if
(
originInputs
[
'QuotaForNewUser'
]
!==
inputs
.
QuotaForNewUser
)
{
await
updateOption
(
'QuotaForNewUser'
,
inputs
.
QuotaForNewUser
);
}
if
(
originInputs
[
'QuotaForInvitee'
]
!==
inputs
.
QuotaForInvitee
)
{
await
updateOption
(
'QuotaForInvitee'
,
inputs
.
QuotaForInvitee
);
}
if
(
originInputs
[
'QuotaForInviter'
]
!==
inputs
.
QuotaForInviter
)
{
await
updateOption
(
'QuotaForInviter'
,
inputs
.
QuotaForInviter
);
}
if
(
originInputs
[
'QuotaRemindThreshold'
]
!==
inputs
.
QuotaRemindThreshold
)
{
await
updateOption
(
'QuotaRemindThreshold'
,
inputs
.
QuotaRemindThreshold
);
}
if
(
originInputs
[
'PreConsumedQuota'
]
!==
inputs
.
PreConsumedQuota
)
{
await
updateOption
(
'PreConsumedQuota'
,
inputs
.
PreConsumedQuota
);
}
if
(
originInputs
[
'ModelRatio'
]
!==
inputs
.
ModelRatio
)
{
if
(
!
verifyJSON
(
inputs
.
ModelRatio
))
{
showError
(
'模型倍率不是合法的 JSON 字符串'
);
return
;
}
await
updateOption
(
'ModelRatio'
,
inputs
.
ModelRatio
);
}
if
(
originInputs
[
'GroupRatio'
]
!==
inputs
.
GroupRatio
)
{
if
(
!
verifyJSON
(
inputs
.
GroupRatio
))
{
showError
(
'分组倍率不是合法的 JSON 字符串'
);
return
;
}
await
updateOption
(
'GroupRatio'
,
inputs
.
GroupRatio
);
}
if
(
originInputs
[
'TopUpLink'
]
!==
inputs
.
TopUpLink
)
{
await
updateOption
(
'TopUpLink'
,
inputs
.
TopUpLink
);
}
if
(
originInputs
[
'ChatLink'
]
!==
inputs
.
ChatLink
)
{
await
updateOption
(
'ChatLink'
,
inputs
.
ChatLink
);
}
};
const
submitSMTP
=
async
()
=>
{
if
(
originInputs
[
'SMTPServer'
]
!==
inputs
.
SMTPServer
)
{
await
updateOption
(
'SMTPServer'
,
inputs
.
SMTPServer
);
...
...
@@ -301,135 +240,6 @@ const SystemSetting = () => {
<
/Form.Group
>
<
Divider
/>
<
Header
as
=
'h3'
>
运营设置
<
/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
=
'TopUpLink'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
TopUpLink
}
type
=
'link'
placeholder
=
'例如发卡网站的购买链接'
/>
<
Form
.
Input
label
=
'额度提醒阈值'
name
=
'QuotaRemindThreshold'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaRemindThreshold
}
type
=
'number'
min
=
'0'
placeholder
=
'低于此额度时将发送邮件提醒用户'
/>
<
Form
.
Input
label
=
'请求预扣费额度'
name
=
'PreConsumedQuota'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
PreConsumedQuota
}
type
=
'number'
min
=
'0'
placeholder
=
'请求结束后多退少补'
/>
<
/Form.Group
>
<
Form
.
Group
widths
=
{
4
}
>
<
Form
.
Input
label
=
'邀请新用户奖励配额'
name
=
'QuotaForInviter'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaForInviter
}
type
=
'number'
min
=
'0'
placeholder
=
'例如:100'
/>
<
Form
.
Input
label
=
'新用户使用邀请码奖励配额'
name
=
'QuotaForInvitee'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
QuotaForInvitee
}
type
=
'number'
min
=
'0'
placeholder
=
'例如:100'
/>
<
Form
.
Input
label
=
'聊天页面链接'
name
=
'ChatLink'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
ChatLink
}
type
=
'link'
placeholder
=
'例如 ChatGPT Next Web 的部署地址'
/>
<
/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
.
Checkbox
checked
=
{
inputs
.
LogConsumeEnabled
===
'true'
}
label
=
'启用额度消费日志记录'
name
=
'LogConsumeEnabled'
onChange
=
{
handleInputChange
}
/
>
<
Form
.
Button
onClick
=
{
submitOperationConfig
}
>
保存运营设置
<
/Form.Button
>
<
Divider
/>
<
Header
as
=
'h3'
>
监控设置
<
/Header
>
<
Form
.
Group
widths
=
{
3
}
>
<
Form
.
Input
label
=
'最长响应时间'
name
=
'ChannelDisableThreshold'
onChange
=
{
handleInputChange
}
autoComplete
=
'new-password'
value
=
{
inputs
.
ChannelDisableThreshold
}
type
=
'number'
min
=
'0'
placeholder
=
'单位秒,当运行通道全部测试时,超过此时间将自动禁用通道'
/>
<
/Form.Group
>
<
Form
.
Group
inline
>
<
Form
.
Checkbox
checked
=
{
inputs
.
AutomaticDisableChannelEnabled
===
'true'
}
label
=
'失败时自动禁用通道'
name
=
'AutomaticDisableChannelEnabled'
onChange
=
{
handleInputChange
}
/
>
<
/Form.Group
>
<
Divider
/>
<
Header
as
=
'h3'
>
配置
SMTP
<
Header
.
Subheader
>
用以支持系统的邮件发送
<
/Header.Subheader
>
<
/Header
>
...
...
web/src/pages/Setting/index.js
View file @
946aa773
...
...
@@ -4,6 +4,7 @@ import SystemSetting from '../../components/SystemSetting';
import
{
isRoot
}
from
'../../helpers'
;
import
OtherSetting
from
'../../components/OtherSetting'
;
import
PersonalSetting
from
'../../components/PersonalSetting'
;
import
OperationSetting
from
'../../components/OperationSetting'
;
const
Setting
=
()
=>
{
let
panes
=
[
...
...
@@ -19,6 +20,14 @@ const Setting = () => {
if
(
isRoot
())
{
panes
.
push
({
menuItem
:
'运营设置'
,
render
:
()
=>
(
<
Tab
.
Pane
attached
=
{
false
}
>
<
OperationSetting
/>
<
/Tab.Pane
>
)
});
panes
.
push
({
menuItem
:
'系统设置'
,
render
:
()
=>
(
<
Tab
.
Pane
attached
=
{
false
}
>
...
...
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