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
Show 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 = ""
...
@@ -52,6 +52,11 @@ var TurnstileSecretKey = ""
var
QuotaForNewUser
=
100
var
QuotaForNewUser
=
100
var
ChannelDisableThreshold
=
5.0
var
AutomaticDisableChannelEnabled
=
false
var
RootUserEmail
=
""
const
(
const
(
RoleGuestUser
=
0
RoleGuestUser
=
0
RoleCommonUser
=
1
RoleCommonUser
=
1
...
...
controller/channel.go
View file @
f43a810e
...
@@ -263,6 +263,20 @@ func TestChannel(c *gin.Context) {
...
@@ -263,6 +263,20 @@ func TestChannel(c *gin.Context) {
var
testAllChannelsLock
sync
.
Mutex
var
testAllChannelsLock
sync
.
Mutex
var
testAllChannelsRunning
bool
=
false
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
{
func
testAllChannels
(
c
*
gin
.
Context
)
error
{
testAllChannelsLock
.
Lock
()
testAllChannelsLock
.
Lock
()
if
testAllChannelsRunning
{
if
testAllChannelsRunning
{
...
@@ -280,8 +294,10 @@ func testAllChannels(c *gin.Context) error {
...
@@ -280,8 +294,10 @@ func testAllChannels(c *gin.Context) error {
return
err
return
err
}
}
testRequest
:=
buildTestRequest
(
c
)
testRequest
:=
buildTestRequest
(
c
)
var
disableThreshold
int64
=
5000
// TODO: make it configurable
var
disableThreshold
=
int64
(
common
.
ChannelDisableThreshold
*
1000
)
email
:=
model
.
GetRootUserEmail
()
if
disableThreshold
==
0
{
disableThreshold
=
10000000
// a impossible value
}
go
func
()
{
go
func
()
{
for
_
,
channel
:=
range
channels
{
for
_
,
channel
:=
range
channels
{
if
channel
.
Status
!=
common
.
ChannelStatusEnabled
{
if
channel
.
Status
!=
common
.
ChannelStatusEnabled
{
...
@@ -295,18 +311,11 @@ func testAllChannels(c *gin.Context) error {
...
@@ -295,18 +311,11 @@ func testAllChannels(c *gin.Context) error {
if
milliseconds
>
disableThreshold
{
if
milliseconds
>
disableThreshold
{
err
=
errors
.
New
(
fmt
.
Sprintf
(
"响应时间 %.2fs 超过阈值 %.2fs"
,
float64
(
milliseconds
)
/
1000.0
,
float64
(
disableThreshold
)
/
1000.0
))
err
=
errors
.
New
(
fmt
.
Sprintf
(
"响应时间 %.2fs 超过阈值 %.2fs"
,
float64
(
milliseconds
)
/
1000.0
,
float64
(
disableThreshold
)
/
1000.0
))
}
}
// disable & notify
disableChannel
(
channel
.
Id
,
channel
.
Name
,
err
)
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
()))
}
}
}
channel
.
UpdateResponseTime
(
milliseconds
)
channel
.
UpdateResponseTime
(
milliseconds
)
}
}
err
:=
common
.
SendEmail
(
"通道测试完成"
,
e
mail
,
"通道测试完成,如果没有收到禁用通知,说明所有通道都正常"
)
err
:=
common
.
SendEmail
(
"通道测试完成"
,
common
.
RootUserE
mail
,
"通道测试完成,如果没有收到禁用通知,说明所有通道都正常"
)
if
err
!=
nil
{
if
err
!=
nil
{
common
.
SysError
(
fmt
.
Sprintf
(
"发送邮件失败:%s"
,
err
.
Error
()))
common
.
SysError
(
fmt
.
Sprintf
(
"发送邮件失败:%s"
,
err
.
Error
()))
}
}
...
...
controller/relay.go
View file @
f43a810e
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"bufio"
"bufio"
"bytes"
"bytes"
"encoding/json"
"encoding/json"
"errors"
"fmt"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
"github.com/pkoukk/tiktoken-go"
"github.com/pkoukk/tiktoken-go"
...
@@ -74,6 +75,11 @@ func Relay(c *gin.Context) {
...
@@ -74,6 +75,11 @@ func Relay(c *gin.Context) {
"type"
:
"one_api_error"
,
"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 {
...
@@ -256,6 +262,10 @@ func relayHelper(c *gin.Context) error {
if
err
!=
nil
{
if
err
!=
nil
{
return
err
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
// Reset response body
resp
.
Body
=
io
.
NopCloser
(
bytes
.
NewBuffer
(
responseBody
))
resp
.
Body
=
io
.
NopCloser
(
bytes
.
NewBuffer
(
responseBody
))
}
}
...
...
middleware/distributor.go
View file @
f43a810e
...
@@ -62,6 +62,8 @@ func Distribute() func(c *gin.Context) {
...
@@ -62,6 +62,8 @@ func Distribute() func(c *gin.Context) {
}
}
}
}
c
.
Set
(
"channel"
,
channel
.
Type
)
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
))
c
.
Request
.
Header
.
Set
(
"Authorization"
,
fmt
.
Sprintf
(
"Bearer %s"
,
channel
.
Key
))
if
channel
.
Type
==
common
.
ChannelTypeCustom
||
channel
.
Type
==
common
.
ChannelTypeAzure
{
if
channel
.
Type
==
common
.
ChannelTypeCustom
||
channel
.
Type
==
common
.
ChannelTypeAzure
{
c
.
Set
(
"base_url"
,
channel
.
BaseURL
)
c
.
Set
(
"base_url"
,
channel
.
BaseURL
)
...
...
model/channel.go
View file @
f43a810e
...
@@ -86,15 +86,15 @@ func (channel *Channel) UpdateResponseTime(responseTime int64) {
...
@@ -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
{
func
(
channel
*
Channel
)
Delete
()
error
{
var
err
error
var
err
error
err
=
DB
.
Delete
(
channel
)
.
Error
err
=
DB
.
Delete
(
channel
)
.
Error
return
err
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() {
...
@@ -32,6 +32,8 @@ func InitOptionMap() {
common
.
OptionMap
[
"WeChatAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
WeChatAuthEnabled
)
common
.
OptionMap
[
"WeChatAuthEnabled"
]
=
strconv
.
FormatBool
(
common
.
WeChatAuthEnabled
)
common
.
OptionMap
[
"TurnstileCheckEnabled"
]
=
strconv
.
FormatBool
(
common
.
TurnstileCheckEnabled
)
common
.
OptionMap
[
"TurnstileCheckEnabled"
]
=
strconv
.
FormatBool
(
common
.
TurnstileCheckEnabled
)
common
.
OptionMap
[
"RegisterEnabled"
]
=
strconv
.
FormatBool
(
common
.
RegisterEnabled
)
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
[
"SMTPServer"
]
=
""
common
.
OptionMap
[
"SMTPFrom"
]
=
""
common
.
OptionMap
[
"SMTPFrom"
]
=
""
common
.
OptionMap
[
"SMTPPort"
]
=
strconv
.
Itoa
(
common
.
SMTPPort
)
common
.
OptionMap
[
"SMTPPort"
]
=
strconv
.
Itoa
(
common
.
SMTPPort
)
...
@@ -114,6 +116,8 @@ func updateOptionMap(key string, value string) (err error) {
...
@@ -114,6 +116,8 @@ func updateOptionMap(key string, value string) (err error) {
common
.
TurnstileCheckEnabled
=
boolValue
common
.
TurnstileCheckEnabled
=
boolValue
case
"RegisterEnabled"
:
case
"RegisterEnabled"
:
common
.
RegisterEnabled
=
boolValue
common
.
RegisterEnabled
=
boolValue
case
"AutomaticDisableChannelEnabled"
:
common
.
AutomaticDisableChannelEnabled
=
boolValue
}
}
}
}
switch
key
{
switch
key
{
...
@@ -156,6 +160,8 @@ func updateOptionMap(key string, value string) (err error) {
...
@@ -156,6 +160,8 @@ func updateOptionMap(key string, value string) (err error) {
err
=
common
.
UpdateModelRatioByJSONString
(
value
)
err
=
common
.
UpdateModelRatioByJSONString
(
value
)
case
"TopUpLink"
:
case
"TopUpLink"
:
common
.
TopUpLink
=
value
common
.
TopUpLink
=
value
case
"ChannelDisableThreshold"
:
common
.
ChannelDisableThreshold
,
_
=
strconv
.
ParseFloat
(
value
,
64
)
}
}
return
err
return
err
}
}
web/src/components/SystemSetting.js
View file @
f43a810e
...
@@ -28,7 +28,9 @@ const SystemSetting = () => {
...
@@ -28,7 +28,9 @@ const SystemSetting = () => {
RegisterEnabled
:
''
,
RegisterEnabled
:
''
,
QuotaForNewUser
:
0
,
QuotaForNewUser
:
0
,
ModelRatio
:
''
,
ModelRatio
:
''
,
TopUpLink
:
''
TopUpLink
:
''
,
AutomaticDisableChannelEnabled
:
''
,
ChannelDisableThreshold
:
0
,
});
});
let
originInputs
=
{};
let
originInputs
=
{};
let
[
loading
,
setLoading
]
=
useState
(
false
);
let
[
loading
,
setLoading
]
=
useState
(
false
);
...
@@ -62,6 +64,7 @@ const SystemSetting = () => {
...
@@ -62,6 +64,7 @@ const SystemSetting = () => {
case
'WeChatAuthEnabled'
:
case
'WeChatAuthEnabled'
:
case
'TurnstileCheckEnabled'
:
case
'TurnstileCheckEnabled'
:
case
'RegisterEnabled'
:
case
'RegisterEnabled'
:
case
'AutomaticDisableChannelEnabled'
:
value
=
inputs
[
key
]
===
'true'
?
'false'
:
'true'
;
value
=
inputs
[
key
]
===
'true'
?
'false'
:
'true'
;
break
;
break
;
default
:
default
:
...
@@ -299,6 +302,30 @@ const SystemSetting = () => {
...
@@ -299,6 +302,30 @@ const SystemSetting = () => {
<
Form
.
Button
onClick
=
{
submitOperationConfig
}
>
保存运营设置
<
/Form.Button
>
<
Form
.
Button
onClick
=
{
submitOperationConfig
}
>
保存运营设置
<
/Form.Button
>
<
Divider
/>
<
Divider
/>
<
Header
as
=
'h3'
>
<
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
配置
SMTP
<
Header
.
Subheader
>
用以支持系统的邮件发送
<
/Header.Subheader
>
<
Header
.
Subheader
>
用以支持系统的邮件发送
<
/Header.Subheader
>
<
/Header
>
<
/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