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
f43a810e
authored
May 15, 2023
by
JustSong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: automatically disable channel when error occurred (#59)
parent
b1074b43
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
78 additions
and
19 deletions
+78
-19
common/constants.go
+5
-0
controller/channel.go
+20
-11
controller/relay.go
+10
-0
middleware/distributor.go
+2
-0
model/channel.go
+7
-7
model/option.go
+6
-0
web/src/components/SystemSetting.js
+28
-1
No files found.
common/constants.go
View file @
f43a810e
...
...
@@ -52,6 +52,11 @@ var TurnstileSecretKey = ""
var
QuotaForNewUser
=
100
var
ChannelDisableThreshold
=
5.0
var
AutomaticDisableChannelEnabled
=
false
var
RootUserEmail
=
""
const
(
RoleGuestUser
=
0
RoleCommonUser
=
1
...
...
controller/channel.go
View file @
f43a810e
...
...
@@ -263,6 +263,20 @@ func TestChannel(c *gin.Context) {
var
testAllChannelsLock
sync
.
Mutex
var
testAllChannelsRunning
bool
=
false
// disable & notify
func
disableChannel
(
channelId
int
,
channelName
string
,
err
error
)
{
if
common
.
RootUserEmail
==
""
{
common
.
RootUserEmail
=
model
.
GetRootUserEmail
()
}
model
.
UpdateChannelStatusById
(
channelId
,
common
.
ChannelStatusDisabled
)
subject
:=
fmt
.
Sprintf
(
"通道「%s」(#%d)已被禁用"
,
channelName
,
channelId
)
content
:=
fmt
.
Sprintf
(
"通道「%s」(#%d)已被禁用,原因:%s"
,
channelName
,
channelId
,
err
.
Error
())
err
=
common
.
SendEmail
(
subject
,
common
.
RootUserEmail
,
content
)
if
err
!=
nil
{
common
.
SysError
(
fmt
.
Sprintf
(
"发送邮件失败:%s"
,
err
.
Error
()))
}
}
func
testAllChannels
(
c
*
gin
.
Context
)
error
{
testAllChannelsLock
.
Lock
()
if
testAllChannelsRunning
{
...
...
@@ -280,8 +294,10 @@ func testAllChannels(c *gin.Context) error {
return
err
}
testRequest
:=
buildTestRequest
(
c
)
var
disableThreshold
int64
=
5000
// TODO: make it configurable
email
:=
model
.
GetRootUserEmail
()
var
disableThreshold
=
int64
(
common
.
ChannelDisableThreshold
*
1000
)
if
disableThreshold
==
0
{
disableThreshold
=
10000000
// a impossible value
}
go
func
()
{
for
_
,
channel
:=
range
channels
{
if
channel
.
Status
!=
common
.
ChannelStatusEnabled
{
...
...
@@ -295,18 +311,11 @@ func testAllChannels(c *gin.Context) error {
if
milliseconds
>
disableThreshold
{
err
=
errors
.
New
(
fmt
.
Sprintf
(
"响应时间 %.2fs 超过阈值 %.2fs"
,
float64
(
milliseconds
)
/
1000.0
,
float64
(
disableThreshold
)
/
1000.0
))
}
// disable & notify
channel
.
UpdateStatus
(
common
.
ChannelStatusDisabled
)
subject
:=
fmt
.
Sprintf
(
"通道「%s」(#%d)已被禁用"
,
channel
.
Name
,
channel
.
Id
)
content
:=
fmt
.
Sprintf
(
"通道「%s」(#%d)已被禁用,原因:%s"
,
channel
.
Name
,
channel
.
Id
,
err
.
Error
())
err
=
common
.
SendEmail
(
subject
,
email
,
content
)
if
err
!=
nil
{
common
.
SysError
(
fmt
.
Sprintf
(
"发送邮件失败:%s"
,
err
.
Error
()))
}
disableChannel
(
channel
.
Id
,
channel
.
Name
,
err
)
}
channel
.
UpdateResponseTime
(
milliseconds
)
}
err
:=
common
.
SendEmail
(
"通道测试完成"
,
e
mail
,
"通道测试完成,如果没有收到禁用通知,说明所有通道都正常"
)
err
:=
common
.
SendEmail
(
"通道测试完成"
,
common
.
RootUserE
mail
,
"通道测试完成,如果没有收到禁用通知,说明所有通道都正常"
)
if
err
!=
nil
{
common
.
SysError
(
fmt
.
Sprintf
(
"发送邮件失败:%s"
,
err
.
Error
()))
}
...
...
controller/relay.go
View file @
f43a810e
...
...
@@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/pkoukk/tiktoken-go"
...
...
@@ -74,6 +75,11 @@ func Relay(c *gin.Context) {
"type"
:
"one_api_error"
,
},
})
if
common
.
AutomaticDisableChannelEnabled
{
channelId
:=
c
.
GetInt
(
"channel_id"
)
channelName
:=
c
.
GetString
(
"channel_name"
)
disableChannel
(
channelId
,
channelName
,
err
)
}
}
}
...
...
@@ -256,6 +262,10 @@ func relayHelper(c *gin.Context) error {
if
err
!=
nil
{
return
err
}
if
textResponse
.
Error
.
Type
!=
""
{
return
errors
.
New
(
fmt
.
Sprintf
(
"type %s, code %s, message %s"
,
textResponse
.
Error
.
Type
,
textResponse
.
Error
.
Code
,
textResponse
.
Error
.
Message
))
}
// Reset response body
resp
.
Body
=
io
.
NopCloser
(
bytes
.
NewBuffer
(
responseBody
))
}
...
...
middleware/distributor.go
View file @
f43a810e
...
...
@@ -62,6 +62,8 @@ func Distribute() func(c *gin.Context) {
}
}
c
.
Set
(
"channel"
,
channel
.
Type
)
c
.
Set
(
"channel_id"
,
channel
.
Id
)
c
.
Set
(
"channel_name"
,
channel
.
Name
)
c
.
Request
.
Header
.
Set
(
"Authorization"
,
fmt
.
Sprintf
(
"Bearer %s"
,
channel
.
Key
))
if
channel
.
Type
==
common
.
ChannelTypeCustom
||
channel
.
Type
==
common
.
ChannelTypeAzure
{
c
.
Set
(
"base_url"
,
channel
.
BaseURL
)
...
...
model/channel.go
View file @
f43a810e
...
...
@@ -86,15 +86,15 @@ func (channel *Channel) UpdateResponseTime(responseTime int64) {
}
}
func
(
channel
*
Channel
)
UpdateStatus
(
status
int
)
{
err
:=
DB
.
Model
(
channel
)
.
Update
(
"status"
,
status
)
.
Error
if
err
!=
nil
{
common
.
SysError
(
"failed to update response time: "
+
err
.
Error
())
}
}
func
(
channel
*
Channel
)
Delete
()
error
{
var
err
error
err
=
DB
.
Delete
(
channel
)
.
Error
return
err
}
func
UpdateChannelStatusById
(
id
int
,
status
int
)
{
err
:=
DB
.
Model
(
&
Channel
{})
.
Where
(
"id = ?"
,
id
)
.
Update
(
"status"
,
status
)
.
Error
if
err
!=
nil
{
common
.
SysError
(
"failed to update channel status: "
+
err
.
Error
())
}
}
model/option.go
View file @
f43a810e
...
...
@@ -32,6 +32,8 @@ func InitOptionMap() {
common
.
OptionMap
[
"WeChatAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
WeChatAuthEnabled
)
common
.
OptionMap
[
"TurnstileCheckEnabled"
]
=
strconv
.
FormatBool
(
common
.
TurnstileCheckEnabled
)
common
.
OptionMap
[
"RegisterEnabled"
]
=
strconv
.
FormatBool
(
common
.
RegisterEnabled
)
common
.
OptionMap
[
"AutomaticDisableChannelEnabled"
]
=
strconv
.
FormatBool
(
common
.
AutomaticDisableChannelEnabled
)
common
.
OptionMap
[
"ChannelDisableThreshold"
]
=
strconv
.
FormatFloat
(
common
.
ChannelDisableThreshold
,
'f'
,
-
1
,
64
)
common
.
OptionMap
[
"SMTPServer"
]
=
""
common
.
OptionMap
[
"SMTPFrom"
]
=
""
common
.
OptionMap
[
"SMTPPort"
]
=
strconv
.
Itoa
(
common
.
SMTPPort
)
...
...
@@ -114,6 +116,8 @@ func updateOptionMap(key string, value string) (err error) {
common
.
TurnstileCheckEnabled
=
boolValue
case
"RegisterEnabled"
:
common
.
RegisterEnabled
=
boolValue
case
"AutomaticDisableChannelEnabled"
:
common
.
AutomaticDisableChannelEnabled
=
boolValue
}
}
switch
key
{
...
...
@@ -156,6 +160,8 @@ func updateOptionMap(key string, value string) (err error) {
err
=
common
.
UpdateModelRatioByJSONString
(
value
)
case
"TopUpLink"
:
common
.
TopUpLink
=
value
case
"ChannelDisableThreshold"
:
common
.
ChannelDisableThreshold
,
_
=
strconv
.
ParseFloat
(
value
,
64
)
}
return
err
}
web/src/components/SystemSetting.js
View file @
f43a810e
...
...
@@ -28,7 +28,9 @@ const SystemSetting = () => {
RegisterEnabled
:
''
,
QuotaForNewUser
:
0
,
ModelRatio
:
''
,
TopUpLink
:
''
TopUpLink
:
''
,
AutomaticDisableChannelEnabled
:
''
,
ChannelDisableThreshold
:
0
,
});
let
originInputs
=
{};
let
[
loading
,
setLoading
]
=
useState
(
false
);
...
...
@@ -62,6 +64,7 @@ const SystemSetting = () => {
case
'WeChatAuthEnabled'
:
case
'TurnstileCheckEnabled'
:
case
'RegisterEnabled'
:
case
'AutomaticDisableChannelEnabled'
:
value
=
inputs
[
key
]
===
'true'
?
'false'
:
'true'
;
break
;
default
:
...
...
@@ -299,6 +302,30 @@ const SystemSetting = () => {
<
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
>
...
...
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