mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-09-17 17:16:38 +08:00
fix: translate error messages and comments to English for consistency
This commit is contained in:
parent
59dba5bef3
commit
d5fa98f2e0
@ -53,7 +53,7 @@ func getLarkUserInfoByCode(code string) (*LarkUser, error) {
|
|||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.SysLog(err.Error())
|
logger.SysLog(err.Error())
|
||||||
return nil, errors.New("None法连接至飞书服务器,请稍后Retry!")
|
return nil, errors.New("Unable to connect to Lark server, please try again later!")
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
var oAuthResponse LarkOAuthResponse
|
var oAuthResponse LarkOAuthResponse
|
||||||
@ -69,7 +69,7 @@ func getLarkUserInfoByCode(code string) (*LarkUser, error) {
|
|||||||
res2, err := client.Do(req)
|
res2, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.SysLog(err.Error())
|
logger.SysLog(err.Error())
|
||||||
return nil, errors.New("None法连接至飞书服务器,请稍后Retry!")
|
return nil, errors.New("Unable to connect to Lark server, please try again later!")
|
||||||
}
|
}
|
||||||
var larkUser LarkUser
|
var larkUser LarkUser
|
||||||
err = json.NewDecoder(res2.Body).Decode(&larkUser)
|
err = json.NewDecoder(res2.Body).Decode(&larkUser)
|
||||||
@ -168,7 +168,7 @@ func LarkBind(c *gin.Context) {
|
|||||||
if model.IsLarkIdAlreadyTaken(user.LarkId) {
|
if model.IsLarkIdAlreadyTaken(user.LarkId) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "该飞书账户已被Bind",
|
"message": "This Lark account has already been bound",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,16 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/controller"
|
"github.com/songquanpeng/one-api/controller"
|
||||||
"github.com/songquanpeng/one-api/model"
|
"github.com/songquanpeng/one-api/model"
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type OidcResponse struct {
|
type OidcResponse struct {
|
||||||
@ -60,7 +61,7 @@ func getOidcUserInfoByCode(code string) (*OidcUser, error) {
|
|||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.SysLog(err.Error())
|
logger.SysLog(err.Error())
|
||||||
return nil, errors.New("None法连接至 OIDC 服务器,请稍后Retry!")
|
return nil, errors.New("Unable to connect to the OIDC server, please try again later!")
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
var oidcResponse OidcResponse
|
var oidcResponse OidcResponse
|
||||||
@ -76,7 +77,7 @@ func getOidcUserInfoByCode(code string) (*OidcUser, error) {
|
|||||||
res2, err := client.Do(req)
|
res2, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.SysLog(err.Error())
|
logger.SysLog(err.Error())
|
||||||
return nil, errors.New("None法连接至 OIDC 服务器,请稍后Retry!")
|
return nil, errors.New("Unable to connect to the OIDC server, please try again later!")
|
||||||
}
|
}
|
||||||
var oidcUser OidcUser
|
var oidcUser OidcUser
|
||||||
err = json.NewDecoder(res2.Body).Decode(&oidcUser)
|
err = json.NewDecoder(res2.Body).Decode(&oidcUser)
|
||||||
@ -104,7 +105,7 @@ func OidcAuth(c *gin.Context) {
|
|||||||
if !config.OidcEnabled {
|
if !config.OidcEnabled {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "Administrator未开启通过 OIDC Log in以及Sign up",
|
"message": "Administrator has not enabled OIDC Log in and Sign up",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -173,7 +174,7 @@ func OidcBind(c *gin.Context) {
|
|||||||
if !config.OidcEnabled {
|
if !config.OidcEnabled {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "Administrator未开启通过 OIDC Log in以及Sign up",
|
"message": "The administrator has turned off new user registration",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -192,7 +193,7 @@ func OidcBind(c *gin.Context) {
|
|||||||
if model.IsOidcIdAlreadyTaken(user.OidcId) {
|
if model.IsOidcIdAlreadyTaken(user.OidcId) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "该 OIDC 账户已被Bind",
|
"message": "This OIDC account has already been bound",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,14 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/songquanpeng/one-api/common"
|
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
|
||||||
"github.com/songquanpeng/one-api/common/message"
|
|
||||||
"github.com/songquanpeng/one-api/model"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/songquanpeng/one-api/common"
|
||||||
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
|
"github.com/songquanpeng/one-api/common/message"
|
||||||
|
"github.com/songquanpeng/one-api/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetStatus(c *gin.Context) {
|
func GetStatus(c *gin.Context) {
|
||||||
@ -100,7 +100,7 @@ func SendEmailVerification(c *gin.Context) {
|
|||||||
if !allowed {
|
if !allowed {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "AdministratorEnable了邮箱域名白名单,您的Email address的域名不在白名单中",
|
"message": "Administrator has enabled email domain whitelist, your email domain is not in the whitelist",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -154,8 +154,8 @@ func SendPasswordResetEmail(c *gin.Context) {
|
|||||||
link := fmt.Sprintf("%s/user/reset?email=%s&token=%s", config.ServerAddress, email, code)
|
link := fmt.Sprintf("%s/user/reset?email=%s&token=%s", config.ServerAddress, email, code)
|
||||||
subject := fmt.Sprintf("%s Password reset", config.SystemName)
|
subject := fmt.Sprintf("%s Password reset", config.SystemName)
|
||||||
content := fmt.Sprintf("<p>Hello, you are resetting %s password.</p>"+
|
content := fmt.Sprintf("<p>Hello, you are resetting %s password.</p>"+
|
||||||
"<p>点击 <a href='%s'>此处</a> 进行Password reset。</p>"+
|
"<p>Click <a href='%s'>here</a> to reset your password.</p>"+
|
||||||
"<p>如果链接None法点击,请尝试点击下面的链接或将其Copy到浏览器中打开:<br> %s </p>"+
|
"<p>If the link cannot be clicked, please try clicking the link below or copy it to your browser to open:<br> %s </p>"+
|
||||||
"<p>The reset link is valid within %d minutes. If it is not your operation, please ignore it.</p>", config.SystemName, link, link, common.VerificationValidMinutes)
|
"<p>The reset link is valid within %d minutes. If it is not your operation, please ignore it.</p>", config.SystemName, link, link, common.VerificationValidMinutes)
|
||||||
err := message.SendEmail(subject, email, content)
|
err := message.SendEmail(subject, email, content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,13 +2,13 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
|
||||||
"github.com/songquanpeng/one-api/model"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
|
"github.com/songquanpeng/one-api/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetOptions(c *gin.Context) {
|
func GetOptions(c *gin.Context) {
|
||||||
@ -47,7 +47,7 @@ func UpdateOption(c *gin.Context) {
|
|||||||
if !config.ValidThemes[option.Value] {
|
if !config.ValidThemes[option.Value] {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "None效的主题",
|
"message": "invalid theme",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ func UpdateOption(c *gin.Context) {
|
|||||||
if option.Value == "true" && config.GitHubClientId == "" {
|
if option.Value == "true" && config.GitHubClientId == "" {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "None法Enable GitHub OAuth,请先填入 GitHub Client Id 以及 GitHub Client Secret!",
|
"message": "Unable to enable GitHub OAuth, please fill in the GitHub Client Id and GitHub Client Secret first!",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ func UpdateOption(c *gin.Context) {
|
|||||||
if option.Value == "true" && len(config.EmailDomainWhitelist) == 0 {
|
if option.Value == "true" && len(config.EmailDomainWhitelist) == 0 {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "None法Enable邮箱域名限制,请先填入限制的邮箱域名!",
|
"message": "Unable to enable email domain restriction, please fill in the restricted email domains first!",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ func AddToken(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": fmt.Sprintf("参数Error:%s", err.Error()),
|
"message": fmt.Sprintf("invalid token: %s", err.Error()),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -336,7 +336,7 @@ func UpdateToken(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": fmt.Sprintf("参数Error:%s", err.Error()),
|
"message": fmt.Sprintf("invalid token: %s", err.Error()),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,7 @@ func GetUserDashboard(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "None法获取Statistics",
|
"message": "Failed to get user dashboard data: " + err.Error(),
|
||||||
"data": nil,
|
"data": nil,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@ -415,7 +415,7 @@ func UpdateUser(c *gin.Context) {
|
|||||||
if myRole <= updatedUser.Role && myRole != model.RoleRootUser {
|
if myRole <= updatedUser.Role && myRole != model.RoleRootUser {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "None权将其他Users权限等级Promote到大于Equals自己的权限等级",
|
"message": "No permission to promote other users to a permission level greater than or equal to your own",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -529,7 +529,7 @@ func DeleteSelf(c *gin.Context) {
|
|||||||
if user.Role == model.RoleRootUser {
|
if user.Role == model.RoleRootUser {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "不能DeleteSuper administrator账户",
|
"message": "Cannot delete super administrator account",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -813,7 +813,7 @@ func AdminTopUp(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if req.Remark == "" {
|
if req.Remark == "" {
|
||||||
req.Remark = fmt.Sprintf("通过 API Recharge %s", common.LogQuota(int64(req.Quota)))
|
req.Remark = fmt.Sprintf("Recharged via API %s", common.LogQuota(int64(req.Quota)))
|
||||||
}
|
}
|
||||||
model.RecordTopupLog(req.UserId, req.Remark, req.Quota)
|
model.RecordTopupLog(req.UserId, req.Remark, req.Quota)
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
@ -107,7 +107,7 @@ func TokenAuth() func(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
if token.Subnet != nil && *token.Subnet != "" {
|
if token.Subnet != nil && *token.Subnet != "" {
|
||||||
if !network.IsIpInSubnets(ctx, c.ClientIP(), *token.Subnet) {
|
if !network.IsIpInSubnets(ctx, c.ClientIP(), *token.Subnet) {
|
||||||
abortWithMessage(c, http.StatusForbidden, fmt.Sprintf("该API Keys只能在指定网段使用:%s,当前 ip:%s", *token.Subnet, c.ClientIP()))
|
abortWithMessage(c, http.StatusForbidden, fmt.Sprintf("This API key can only be used in the specified subnet: %s, current IP: %s", *token.Subnet, c.ClientIP()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,7 +129,7 @@ func TokenAuth() func(c *gin.Context) {
|
|||||||
if token.Models != nil && *token.Models != "" {
|
if token.Models != nil && *token.Models != "" {
|
||||||
c.Set(ctxkey.AvailableModels, *token.Models)
|
c.Set(ctxkey.AvailableModels, *token.Models)
|
||||||
if requestModel != "" && !isModelInList(requestModel, *token.Models) {
|
if requestModel != "" && !isModelInList(requestModel, *token.Models) {
|
||||||
abortWithMessage(c, http.StatusForbidden, fmt.Sprintf("该API KeysNone权使用Model:%s", requestModel))
|
abortWithMessage(c, http.StatusForbidden, fmt.Sprintf("This API key does not have permission to use the model: %s", requestModel))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,12 @@ func Distribute() func(c *gin.Context) {
|
|||||||
if ok {
|
if ok {
|
||||||
id, err := strconv.Atoi(channelId.(string))
|
id, err := strconv.Atoi(channelId.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
abortWithMessage(c, http.StatusBadRequest, "None效的Channel Id")
|
abortWithMessage(c, http.StatusBadRequest, "Invalid Channel Id")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
channel, err = model.GetChannelById(id, true)
|
channel, err = model.GetChannelById(id, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
abortWithMessage(c, http.StatusBadRequest, "None效的Channel Id")
|
abortWithMessage(c, http.StatusBadRequest, "Invalid Channel Id")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if channel.Status != model.ChannelStatusEnabled {
|
if channel.Status != model.ChannelStatusEnabled {
|
||||||
@ -47,7 +47,7 @@ func Distribute() func(c *gin.Context) {
|
|||||||
var err error
|
var err error
|
||||||
channel, err = model.CacheGetRandomSatisfiedChannel(userGroup, requestModel, false)
|
channel, err = model.CacheGetRandomSatisfiedChannel(userGroup, requestModel, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
message := fmt.Sprintf("当前Group %s 下对于Model %s No available channels", userGroup, requestModel)
|
message := fmt.Sprintf("No available channels for Model %s under Group %s", requestModel, userGroup)
|
||||||
if channel != nil {
|
if channel != nil {
|
||||||
logger.SysError(fmt.Sprintf("Channel does not exist: %d", channel.Id))
|
logger.SysError(fmt.Sprintf("Channel does not exist: %d", channel.Id))
|
||||||
message = "Database consistency has been broken, please contact the administrator"
|
message = "Database consistency has been broken, please contact the administrator"
|
||||||
|
@ -2,6 +2,7 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
@ -80,7 +81,7 @@ func Redeem(key string, userId int) (quota int64, err error) {
|
|||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errors.New("Redeem失败," + err.Error())
|
return 0, errors.New("Redeem failed, " + err.Error())
|
||||||
}
|
}
|
||||||
RecordLog(userId, LogTypeTopup, fmt.Sprintf("Recharge %s through redemption code", common.LogQuota(redemption.Quota)))
|
RecordLog(userId, LogTypeTopup, fmt.Sprintf("Recharge %s through redemption code", common.LogQuota(redemption.Quota)))
|
||||||
return redemption.Quota, nil
|
return redemption.Quota, nil
|
||||||
|
@ -72,7 +72,7 @@ func ValidateUserToken(key string) (token *Token, err error) {
|
|||||||
return nil, errors.Wrap(err, "failed to get token by key")
|
return nil, errors.Wrap(err, "failed to get token by key")
|
||||||
}
|
}
|
||||||
if token.Status == TokenStatusExhausted {
|
if token.Status == TokenStatusExhausted {
|
||||||
return nil, fmt.Errorf("API Keys %s(#%d)Quota已用尽", token.Name, token.Id)
|
return nil, fmt.Errorf("API Key %s (#%d) quota has been exhausted", token.Name, token.Id)
|
||||||
} else if token.Status == TokenStatusExpired {
|
} else if token.Status == TokenStatusExpired {
|
||||||
return nil, errors.New("The token has expired")
|
return nil, errors.New("The token has expired")
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package monitor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/common/message"
|
"github.com/songquanpeng/one-api/common/message"
|
||||||
@ -38,9 +39,10 @@ func DisableChannel(channelId int, channelName string, reason string) {
|
|||||||
func MetricDisableChannel(channelId int, successRate float64) {
|
func MetricDisableChannel(channelId int, successRate float64) {
|
||||||
model.UpdateChannelStatusById(channelId, model.ChannelStatusAutoDisabled)
|
model.UpdateChannelStatusById(channelId, model.ChannelStatusAutoDisabled)
|
||||||
logger.SysLog(fmt.Sprintf("channel #%d has been disabled due to low success rate: %.2f", channelId, successRate*100))
|
logger.SysLog(fmt.Sprintf("channel #%d has been disabled due to low success rate: %.2f", channelId, successRate*100))
|
||||||
subject := fmt.Sprintf("Channel #%d 已被Disable", channelId)
|
subject := fmt.Sprintf("Channel #%d has been disabled", channelId)
|
||||||
content := fmt.Sprintf("该Channel(#%d)在最近 %d 次调用中成功率为 %.2f%%,低于阈值 %.2f%%,因此被System自动Disable。",
|
content := fmt.Sprintf("The channel (#%d) has been automatically disabled by the system due to "+
|
||||||
channelId, config.MetricQueueSize, successRate*100, config.MetricSuccessRateThreshold*100)
|
"a success rate of %.2f%% over the last %d calls, which is below the threshold of %.2f%%.",
|
||||||
|
channelId, successRate*100, config.MetricQueueSize, config.MetricSuccessRateThreshold*100)
|
||||||
notifyRootUser(subject, content)
|
notifyRootUser(subject, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ func MetricDisableChannel(channelId int, successRate float64) {
|
|||||||
func EnableChannel(channelId int, channelName string) {
|
func EnableChannel(channelId int, channelName string) {
|
||||||
model.UpdateChannelStatusById(channelId, model.ChannelStatusEnabled)
|
model.UpdateChannelStatusById(channelId, model.ChannelStatusEnabled)
|
||||||
logger.SysLog(fmt.Sprintf("channel #%d has been enabled", channelId))
|
logger.SysLog(fmt.Sprintf("channel #%d has been enabled", channelId))
|
||||||
subject := fmt.Sprintf("Channel「%s」(#%d)已被Enable", channelName, channelId)
|
subject := fmt.Sprintf("Channel %s (#%d) has been enabled", channelName, channelId)
|
||||||
content := fmt.Sprintf("Channel「%s」(#%d)已被Enable", channelName, channelId)
|
content := fmt.Sprintf("Channel %s (#%d) has been enabled", channelName, channelId)
|
||||||
notifyRootUser(subject, content)
|
notifyRootUser(subject, content)
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ func ShouldDisableChannel(err *model.Error, statusCode int) bool {
|
|||||||
strings.Contains(lowerMessage, "balance") ||
|
strings.Contains(lowerMessage, "balance") ||
|
||||||
strings.Contains(lowerMessage, "permission denied") ||
|
strings.Contains(lowerMessage, "permission denied") ||
|
||||||
strings.Contains(lowerMessage, "organization has been restricted") || // groq
|
strings.Contains(lowerMessage, "organization has been restricted") || // groq
|
||||||
strings.Contains(lowerMessage, "已欠费") {
|
strings.Contains(lowerMessage, "overdue payment") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -38,7 +38,7 @@ func aiProxyDocuments2Markdown(documents []LibraryDocument) string {
|
|||||||
if len(documents) == 0 {
|
if len(documents) == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
content := "\n\n参考文档:\n"
|
content := "\n\nReference Documents:\n"
|
||||||
for i, document := range documents {
|
for i, document := range documents {
|
||||||
content += fmt.Sprintf("%d. [%s](%s)\n", i+1, document.Title, document.URL)
|
content += fmt.Sprintf("%d. [%s](%s)\n", i+1, document.Title, document.URL)
|
||||||
}
|
}
|
||||||
|
@ -148,18 +148,18 @@ func responseAli2OpenAIImage(response *TaskResponse, responseFormat string) *ope
|
|||||||
for _, data := range response.Output.Results {
|
for _, data := range response.Output.Results {
|
||||||
var b64Json string
|
var b64Json string
|
||||||
if responseFormat == "b64_json" {
|
if responseFormat == "b64_json" {
|
||||||
// 读取 data.Url 的图片数据并转存到 b64Json
|
// Read the image data from data.Url and store it in b64Json
|
||||||
imageData, err := getImageData(data.Url)
|
imageData, err := getImageData(data.Url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 处理获取图片数据失败的情况
|
// Handle the case where getting image data fails
|
||||||
logger.SysError("getImageData Error getting image data: " + err.Error())
|
logger.SysError("getImageData Error getting image data: " + err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将图片数据转为 Base64 编码的字符串
|
// Convert the image data to a Base64 encoded string
|
||||||
b64Json = Base64Encode(imageData)
|
b64Json = Base64Encode(imageData)
|
||||||
} else {
|
} else {
|
||||||
// 如果 responseFormat 不是 "b64_json",则直接使用 data.B64Image
|
// If responseFormat is not "b64_json", use data.B64Image directly
|
||||||
b64Json = data.B64Image
|
b64Json = data.B64Image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,23 +2,23 @@ package cohere
|
|||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Message string `json:"message" required:"true"`
|
Message string `json:"message" required:"true"`
|
||||||
Model string `json:"model,omitempty"` // Default值为"command-r"
|
Model string `json:"model,omitempty"` // default to "command-r"
|
||||||
Stream bool `json:"stream,omitempty"` // Default值为false
|
Stream bool `json:"stream,omitempty"` // default to false
|
||||||
Preamble string `json:"preamble,omitempty"`
|
Preamble string `json:"preamble,omitempty"`
|
||||||
ChatHistory []ChatMessage `json:"chat_history,omitempty"`
|
ChatHistory []ChatMessage `json:"chat_history,omitempty"`
|
||||||
ConversationID string `json:"conversation_id,omitempty"`
|
ConversationID string `json:"conversation_id,omitempty"`
|
||||||
PromptTruncation string `json:"prompt_truncation,omitempty"` // Default值为"AUTO"
|
PromptTruncation string `json:"prompt_truncation,omitempty"` // default to "AUTO"
|
||||||
Connectors []Connector `json:"connectors,omitempty"`
|
Connectors []Connector `json:"connectors,omitempty"`
|
||||||
Documents []Document `json:"documents,omitempty"`
|
Documents []Document `json:"documents,omitempty"`
|
||||||
Temperature *float64 `json:"temperature,omitempty"` // Default值为0.3
|
Temperature *float64 `json:"temperature,omitempty"` // default to 0.3
|
||||||
MaxTokens int `json:"max_tokens,omitempty"`
|
MaxTokens int `json:"max_tokens,omitempty"`
|
||||||
MaxInputTokens int `json:"max_input_tokens,omitempty"`
|
MaxInputTokens int `json:"max_input_tokens,omitempty"`
|
||||||
K int `json:"k,omitempty"` // Default值为0
|
K int `json:"k,omitempty"` // default to 0
|
||||||
P *float64 `json:"p,omitempty"` // Default值为0.75
|
P *float64 `json:"p,omitempty"` // default to 0.75
|
||||||
Seed int `json:"seed,omitempty"`
|
Seed int `json:"seed,omitempty"`
|
||||||
StopSequences []string `json:"stop_sequences,omitempty"`
|
StopSequences []string `json:"stop_sequences,omitempty"`
|
||||||
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"` // Default值为0.0
|
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"` // default to 0.0
|
||||||
PresencePenalty *float64 `json:"presence_penalty,omitempty"` // Default值为0.0
|
PresencePenalty *float64 `json:"presence_penalty,omitempty"` // default to 0.0
|
||||||
Tools []Tool `json:"tools,omitempty"`
|
Tools []Tool `json:"tools,omitempty"`
|
||||||
ToolResults []ToolResult `json:"tool_results,omitempty"`
|
ToolResults []ToolResult `json:"tool_results,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -6,40 +6,46 @@ type Message struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChatRequest struct {
|
type ChatRequest struct {
|
||||||
// Model name,Optional values包括 hunyuan-lite、hunyuan-standard、hunyuan-standard-256K、hunyuan-pro。
|
// Model name, optional values include hunyuan-lite, hunyuan-standard, hunyuan-standard-256K, hunyuan-pro.
|
||||||
// 各Model介绍请阅读 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 中的说明。
|
// For descriptions of each model, please read the [Product Overview](https://cloud.tencent.com/document/product/1729/104753).
|
||||||
//
|
//
|
||||||
// Note:
|
// Note:
|
||||||
// 不同的Model计费不同,请根据 [购买指南](https://cloud.tencent.com/document/product/1729/97731) 按需调用。
|
// Different models have different pricing. Please refer to the [Purchase Guide](https://cloud.tencent.com/document/product/1729/97731) for details.
|
||||||
Model *string `json:"Model"`
|
Model *string `json:"Model"`
|
||||||
// Chat上下文信息。
|
// Chat context information.
|
||||||
// 说明:
|
// Description:
|
||||||
// 1. 长度最多为 40,按对话Time从旧到新在数Group中排列。
|
// 1. The maximum length is 40, arranged in the array in chronological order from oldest to newest.
|
||||||
// 2. Message.Role Optional values:system、user、assistant。
|
// 2. Message.Role optional values: system, user, assistant.
|
||||||
// 其中,system 角色可选,如存在则必须位于列表的最开始。user 和 assistant 需交替出现(一问一答),以 user 提问开始和结束,且 Content 不能为空。Role 的顺序示例:[system(可选) user assistant user assistant user ...]。
|
// Among them, the system role is optional. If it exists, it must be at the beginning of the list.
|
||||||
// 3. Messages 中 Content 总长度不能超过ModelEnter长度上限(可参考 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 文档),超过则会截断最前面的内容,只保留尾部内容。
|
// User and assistant must alternate (one question and one answer), starting and ending with user,
|
||||||
|
// and Content cannot be empty. The order of roles is as follows: [system (optional) user assistant user assistant user ...].
|
||||||
|
// 3. The total length of Content in Messages cannot exceed the model's length limit
|
||||||
|
// (refer to the [Product Overview](https://cloud.tencent.com/document/product/1729/104753) document).
|
||||||
|
// If it exceeds, the earliest content will be truncated, leaving only the latest content.
|
||||||
Messages []*Message `json:"Messages"`
|
Messages []*Message `json:"Messages"`
|
||||||
// 流式调用开关。
|
// Stream call switch.
|
||||||
// 说明:
|
// Description:
|
||||||
// 1. 未传值时Default为非流式调用(false)。
|
// 1. If not provided, the default is non-streaming call (false).
|
||||||
// 2. 流式调用时以 SSE 协议增量返回结果(返回值取 Choices[n].Delta 中的值,需要拼接增量数据才能获得完整结果)。
|
// 2. In streaming calls, results are returned incrementally using the SSE protocol
|
||||||
// 3. 非流式调用时:
|
// (the return value is taken from Choices[n].Delta, and incremental data needs to be concatenated to obtain the complete result).
|
||||||
// 调用方式与普通 HTTP 请求None异。
|
// 3. In non-streaming calls:
|
||||||
// 接口响应耗时较长,**如需更低时延建议Settings为 true**。
|
// The call method is the same as a regular HTTP request.
|
||||||
// 只返回一次最终结果(返回值取 Choices[n].Message 中的值)。
|
// The interface response time is relatively long. **If lower latency is required, it is recommended to set this to true**.
|
||||||
|
// Only the final result is returned once (the return value is taken from Choices[n].Message).
|
||||||
//
|
//
|
||||||
// Note:
|
// Note:
|
||||||
// 通过 SDK 调用时,流式和非流式调用需用**不同的方式**获取返回值,具体参考 SDK 中的注释或示例(在各语言 SDK 代码仓库的 examples/hunyuan/v20230901/ 目录中)。
|
// When calling through the SDK, different methods are required to obtain return values for streaming and non-streaming calls.
|
||||||
|
// Refer to the comments or examples in the SDK (in the examples/hunyuan/v20230901/ directory of each language SDK code repository).
|
||||||
Stream *bool `json:"Stream"`
|
Stream *bool `json:"Stream"`
|
||||||
// 说明:
|
// Description:
|
||||||
// 1. 影响输出文本的多样性,取值越大,生成文本的多样性越强。
|
// 1. Affects the diversity of the output text. The larger the value, the more diverse the generated text.
|
||||||
// 2. 取值区间为 [0.0, 1.0],未传值时使用各Model推荐值。
|
// 2. The value range is [0.0, 1.0]. If not provided, the recommended value for each model is used.
|
||||||
// 3. 非必要不建议使用,不合理的取值会影响效果。
|
// 3. It is not recommended to use this unless necessary, as unreasonable values can affect the results.
|
||||||
TopP *float64 `json:"TopP"`
|
TopP *float64 `json:"TopP"`
|
||||||
// 说明:
|
// Description:
|
||||||
// 1. 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。
|
// 1. Higher values make the output more random, while lower values make it more focused and deterministic.
|
||||||
// 2. 取值区间为 [0.0, 2.0],未传值时使用各Model推荐值。
|
// 2. The value range is [0.0, 2.0]. If not provided, the recommended value for each model is used.
|
||||||
// 3. 非必要不建议使用,不合理的取值会影响效果。
|
// 3. It is not recommended to use this unless necessary, as unreasonable values can affect the results.
|
||||||
Temperature *float64 `json:"Temperature"`
|
Temperature *float64 `json:"Temperature"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,19 +61,19 @@ type Usage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ResponseChoices struct {
|
type ResponseChoices struct {
|
||||||
FinishReason string `json:"FinishReason,omitempty"` // 流式结束标志位,为 stop 则表示尾包
|
FinishReason string `json:"FinishReason,omitempty"` // Stream end flag, "stop" indicates the end packet
|
||||||
Messages Message `json:"Message,omitempty"` // 内容,同步模式返回内容,流模式为 null 输出 content 内容总数最多支持 1024token。
|
Messages Message `json:"Message,omitempty"` // Content, returned in synchronous mode, null in stream mode. The total content supports up to 1024 tokens.
|
||||||
Delta Message `json:"Delta,omitempty"` // 内容,流模式返回内容,同步模式为 null 输出 content 内容总数最多支持 1024token。
|
Delta Message `json:"Delta,omitempty"` // Content, returned in stream mode, null in synchronous mode. The total content supports up to 1024 tokens.
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatResponse struct {
|
type ChatResponse struct {
|
||||||
Choices []ResponseChoices `json:"Choices,omitempty"` // 结果
|
Choices []ResponseChoices `json:"Choices,omitempty"` // Results
|
||||||
Created int64 `json:"Created,omitempty"` // unix Time戳的字符串
|
Created int64 `json:"Created,omitempty"` // Unix timestamp string
|
||||||
Id string `json:"Id,omitempty"` // 会话 id
|
Id string `json:"Id,omitempty"` // Session ID
|
||||||
Usage Usage `json:"Usage,omitempty"` // token 数量
|
Usage Usage `json:"Usage,omitempty"` // Token count
|
||||||
Error Error `json:"Error,omitempty"` // 错误信息 Note:此字段可能返回 null,表示取不到有效值
|
Error Error `json:"Error,omitempty"` // Error information. Note: This field may return null, indicating that no valid value was found.
|
||||||
Note string `json:"Note,omitempty"` // 注释
|
Note string `json:"Note,omitempty"` // Note
|
||||||
ReqID string `json:"Req_id,omitempty"` // 唯一请求 Id,每次请求都会返回。用于反馈接口入参
|
ReqID string `json:"Req_id,omitempty"` // Unique request ID, returned with each request. Used for feedback on interface input parameters.
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatResponseP struct {
|
type ChatResponseP struct {
|
||||||
|
@ -385,8 +385,9 @@ func GetAudioCompletionRatio(actualModelName string) float64 {
|
|||||||
|
|
||||||
// AudioTokensPerSecond is the number of audio tokens per second for each model.
|
// AudioTokensPerSecond is the number of audio tokens per second for each model.
|
||||||
var AudioPromptTokensPerSecond = map[string]float64{
|
var AudioPromptTokensPerSecond = map[string]float64{
|
||||||
// whisper 的 API 价格是 $0.0001/sec。one-api 的历史倍率为 15,对应 $0.03/kilo_tokens。
|
// Whisper API price is $0.0001/sec. One-api's historical ratio is 15,
|
||||||
// 那么换算后可得,每秒的 tokens 应该为 0.0001/0.03*1000 = 3.3333
|
// corresponding to $0.03/kilo_tokens.
|
||||||
|
// After conversion, tokens per second should be 0.0001/0.03*1000 = 3.3333.
|
||||||
"whisper-1": 0.0001 / 0.03 * 1000,
|
"whisper-1": 0.0001 / 0.03 * 1000,
|
||||||
// gpt-4o-audio series processes 10 tokens per second
|
// gpt-4o-audio series processes 10 tokens per second
|
||||||
"gpt-4o-audio-preview": 10,
|
"gpt-4o-audio-preview": 10,
|
||||||
|
@ -125,7 +125,7 @@ func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *meta.M
|
|||||||
}
|
}
|
||||||
var extraLog string
|
var extraLog string
|
||||||
if systemPromptReset {
|
if systemPromptReset {
|
||||||
extraLog = " (NoteSystemPrompt词已被重置)"
|
extraLog = " (Note: System prompt has been reset)"
|
||||||
}
|
}
|
||||||
logContent := fmt.Sprintf("model rate %.2f, group rate %.2f, completion rate %.2f%s", modelRatio, groupRatio, completionRatio, extraLog)
|
logContent := fmt.Sprintf("model rate %.2f, group rate %.2f, completion rate %.2f%s", modelRatio, groupRatio, completionRatio, extraLog)
|
||||||
model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent)
|
model.RecordConsumeLog(ctx, meta.UserId, meta.ChannelId, promptTokens, completionTokens, textRequest.Model, meta.TokenName, quota, logContent)
|
||||||
|
@ -203,7 +203,7 @@ const ChannelsTable = () => {
|
|||||||
trigger={<Label basic color='red'>
|
trigger={<Label basic color='red'>
|
||||||
Disabled
|
Disabled
|
||||||
</Label>}
|
</Label>}
|
||||||
content='本Channel被手动Disable'
|
content='This channel has been manually disabled'
|
||||||
basic
|
basic
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -213,7 +213,7 @@ const ChannelsTable = () => {
|
|||||||
trigger={<Label basic color='yellow'>
|
trigger={<Label basic color='yellow'>
|
||||||
Disabled
|
Disabled
|
||||||
</Label>}
|
</Label>}
|
||||||
content='本Channel被程序自动Disable'
|
content='This channel has been automatically disabled by the program'
|
||||||
basic
|
basic
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -277,7 +277,7 @@ const ChannelsTable = () => {
|
|||||||
newChannels[realIdx].response_time = time * 1000;
|
newChannels[realIdx].response_time = time * 1000;
|
||||||
newChannels[realIdx].test_time = Date.now() / 1000;
|
newChannels[realIdx].test_time = Date.now() / 1000;
|
||||||
setChannels(newChannels);
|
setChannels(newChannels);
|
||||||
showInfo(`Channel ${name} Test成功,Model ${model},耗时 ${time.toFixed(2)}s。`);
|
showInfo(`Channel ${name} tested successfully with model ${model}, taking ${time.toFixed(2)} seconds.`);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -292,7 +292,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.get(`/api/channel/test?scope=${scope}`);
|
const res = await API.get(`/api/channel/test?scope=${scope}`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已成功开始TestChannel,请Refresh页面查看结果。');
|
showInfo('Successfully started testing channels, please refresh the page to see the results.');
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -302,7 +302,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.delete(`/api/channel/disabled`);
|
const res = await API.delete(`/api/channel/disabled`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showSuccess(`已Delete所有DisableChannel,共计 ${data} 个`);
|
showSuccess(`Successfully deleted all disabled channels, total ${data} channels`);
|
||||||
await refresh();
|
await refresh();
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
@ -504,26 +504,26 @@ const ChannelsTable = () => {
|
|||||||
idx,
|
idx,
|
||||||
event.target.value
|
event.target.value
|
||||||
);
|
);
|
||||||
}}>
|
}}>
|
||||||
<input style={{ maxWidth: '60px' }} />
|
<input style={{ maxWidth: '60px' }} />
|
||||||
</Input>}
|
</Input>}
|
||||||
content='Channel priority - higher value means higher priority'
|
content='Channel priority - higher value means higher priority'
|
||||||
/>
|
/>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell hidden={!showDetail}>
|
<Table.Cell hidden={!showDetail}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
placeholder='请选择TestModel'
|
placeholder='Please select TestModel'
|
||||||
selection
|
selection
|
||||||
options={channel.model_options}
|
options={channel.model_options}
|
||||||
defaultValue={channel.test_model}
|
defaultValue={channel.test_model}
|
||||||
onChange={(event, data) => {
|
onChange={(event, data) => {
|
||||||
switchTestModel(idx, data.value);
|
switchTestModel(idx, data.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
size={'small'}
|
size={'small'}
|
||||||
positive
|
positive
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -597,14 +597,14 @@ const ChannelsTable = () => {
|
|||||||
Test all channels
|
Test all channels
|
||||||
</Button>
|
</Button>
|
||||||
<Button size='small' loading={loading} onClick={()=>{testChannels("disabled")}}>
|
<Button size='small' loading={loading} onClick={()=>{testChannels("disabled")}}>
|
||||||
TestDisableChannel
|
Test disabled channels
|
||||||
</Button>
|
</Button>
|
||||||
{/*<Button size='small' onClick={updateAllChannelsBalance}*/}
|
{/*<Button size='small' onClick={updateAllChannelsBalance}*/}
|
||||||
{/* loading={loading || updatingBalance}>Update the balance of enabled channels</Button>*/}
|
{/* loading={loading || updatingBalance}>Update the balance of enabled channels</Button>*/}
|
||||||
<Popup
|
<Popup
|
||||||
trigger={
|
trigger={
|
||||||
<Button size='small' loading={loading}>
|
<Button size='small' loading={loading}>
|
||||||
DeleteDisableChannel
|
Delete disabled channels
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
on='click'
|
on='click'
|
||||||
@ -627,7 +627,7 @@ const ChannelsTable = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Button size='small' onClick={refresh} loading={loading}>Refresh</Button>
|
<Button size='small' onClick={refresh} loading={loading}>Refresh</Button>
|
||||||
<Button size='small' onClick={toggleShowDetail}>{showDetail ? "隐藏Details" : "Details"}</Button>
|
<Button size='small' onClick={toggleShowDetail}>{showDetail ? "Hide Details" : "Details"}</Button>
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Footer>
|
</Table.Footer>
|
||||||
|
@ -247,24 +247,24 @@ const OperationSetting = () => {
|
|||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={() => {
|
<Form.Button onClick={() => {
|
||||||
deleteHistoryLogs().then();
|
deleteHistoryLogs().then();
|
||||||
}}>Clear History Logs</Form.Button>
|
}}>Clear History Logs</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
Monitoring Settings
|
Monitoring Settings
|
||||||
</Header>
|
</Header>
|
||||||
<Form.Group widths={3}>
|
<Form.Group widths={3}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Longest Response Time'
|
label='Maximum Response Time'
|
||||||
name='ChannelDisableThreshold'
|
name='ChannelDisableThreshold'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
value={inputs.ChannelDisableThreshold}
|
value={inputs.ChannelDisableThreshold}
|
||||||
type='number'
|
type='number'
|
||||||
min='0'
|
min='0'
|
||||||
placeholder='Unit in seconds,When all operating channels are tested,Channels will be automatically disabled if this time is exceeded'
|
placeholder='Unit in seconds. When all operating channels are tested, channels will be automatically disabled if this time is exceeded'
|
||||||
/>
|
/>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Quota reminder threshold'
|
label='Quota Reminder Threshold'
|
||||||
name='QuotaRemindThreshold'
|
name='QuotaRemindThreshold'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -273,8 +273,8 @@ const OperationSetting = () => {
|
|||||||
min='0'
|
min='0'
|
||||||
placeholder='Email will be sent to remind users when the quota is below this'
|
placeholder='Email will be sent to remind users when the quota is below this'
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.AutomaticDisableChannelEnabled === 'true'}
|
checked={inputs.AutomaticDisableChannelEnabled === 'true'}
|
||||||
label='Automatically disable the channel when it fails'
|
label='Automatically disable the channel when it fails'
|
||||||
@ -283,12 +283,12 @@ const OperationSetting = () => {
|
|||||||
/>
|
/>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.AutomaticEnableChannelEnabled === 'true'}
|
checked={inputs.AutomaticEnableChannelEnabled === 'true'}
|
||||||
label='成功时自动EnableChannel'
|
label='Automatically enable the channel when it succeeds'
|
||||||
name='AutomaticEnableChannelEnabled'
|
name='AutomaticEnableChannelEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={() => {
|
<Form.Button onClick={() => {
|
||||||
submitConfig('monitor').then();
|
submitConfig('monitor').then();
|
||||||
}}>Save Monitoring Settings</Form.Button>
|
}}>Save Monitoring Settings</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@ -249,7 +249,7 @@ const PersonalSetting = () => {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
status.lark_client_id && (
|
status.lark_client_id && (
|
||||||
<Button onClick={()=>{onLarkOAuthClicked(status.lark_client_id)}}>Bind飞书账号</Button>
|
<Button onClick={()=>{onLarkOAuthClicked(status.lark_client_id)}}>Bind Lark Account</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<Button
|
<Button
|
||||||
|
@ -264,7 +264,7 @@ const SystemSetting = () => {
|
|||||||
<Form.Group widths='equal'>
|
<Form.Group widths='equal'>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Server Address'
|
label='Server Address'
|
||||||
placeholder='For example:https://yourdomain.com'
|
placeholder='For example: https://yourdomain.com'
|
||||||
value={inputs.ServerAddress}
|
value={inputs.ServerAddress}
|
||||||
name='ServerAddress'
|
name='ServerAddress'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
@ -290,7 +290,7 @@ const SystemSetting = () => {
|
|||||||
size={'tiny'}
|
size={'tiny'}
|
||||||
style={{ maxWidth: '450px' }}
|
style={{ maxWidth: '450px' }}
|
||||||
>
|
>
|
||||||
<Modal.Header>警告</Modal.Header>
|
<Modal.Header>Warning</Modal.Header>
|
||||||
<Modal.Content>
|
<Modal.Content>
|
||||||
<p>Canceling password login will cause all users (including administrators) who have not bound other login methods to be unable to log in via password, confirm cancel?</p>
|
<p>Canceling password login will cause all users (including administrators) who have not bound other login methods to be unable to log in via password, confirm cancel?</p>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
@ -303,7 +303,7 @@ const SystemSetting = () => {
|
|||||||
await updateOption('PasswordLoginEnabled', 'false');
|
await updateOption('PasswordLoginEnabled', 'false');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
确定
|
Confirm
|
||||||
</Button>
|
</Button>
|
||||||
</Modal.Actions>
|
</Modal.Actions>
|
||||||
</Modal>
|
</Modal>
|
||||||
@ -336,7 +336,7 @@ const SystemSetting = () => {
|
|||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.RegisterEnabled === 'true'}
|
checked={inputs.RegisterEnabled === 'true'}
|
||||||
label='Allow new user registration (if this option is off, new users will not be able to register in any way)'
|
label='Allow new user registration (if this option is off, new users will not be able to register in any way)'
|
||||||
name='RegisterEnabled'
|
name='RegisterEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
@ -349,12 +349,12 @@ const SystemSetting = () => {
|
|||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
配置邮箱域名白名单
|
Configure Email Domain Whitelist
|
||||||
<Header.Subheader>用以防止恶意Users利用临时邮箱批量Sign up</Header.Subheader>
|
<Header.Subheader>To prevent malicious users from using temporary emails to sign up in bulk</Header.Subheader>
|
||||||
</Header>
|
</Header>
|
||||||
<Form.Group widths={3}>
|
<Form.Group widths={3}>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
label='Enable邮箱域名白名单'
|
label='Enable Email Domain Whitelist'
|
||||||
name='EmailDomainRestrictionEnabled'
|
name='EmailDomainRestrictionEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
checked={inputs.EmailDomainRestrictionEnabled === 'true'}
|
checked={inputs.EmailDomainRestrictionEnabled === 'true'}
|
||||||
@ -362,8 +362,8 @@ const SystemSetting = () => {
|
|||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Group widths={2}>
|
<Form.Group widths={2}>
|
||||||
<Form.Dropdown
|
<Form.Dropdown
|
||||||
label='允许的邮箱域名'
|
label='Allowed Email Domains'
|
||||||
placeholder='允许的邮箱域名'
|
placeholder='Allowed Email Domains'
|
||||||
name='EmailDomainWhitelist'
|
name='EmailDomainWhitelist'
|
||||||
required
|
required
|
||||||
fluid
|
fluid
|
||||||
@ -375,11 +375,11 @@ const SystemSetting = () => {
|
|||||||
options={EmailDomainWhitelist}
|
options={EmailDomainWhitelist}
|
||||||
/>
|
/>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='添加新的允许的邮箱域名'
|
label='Add New Allowed Email Domain'
|
||||||
action={
|
action={
|
||||||
<Button type='button' onClick={() => {
|
<Button type='button' onClick={() => {
|
||||||
submitNewRestrictedDomain();
|
submitNewRestrictedDomain();
|
||||||
}}>填入</Button>
|
}}>Add</Button>
|
||||||
}
|
}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
@ -387,14 +387,14 @@ const SystemSetting = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
placeholder='Enter新的允许的邮箱域名'
|
placeholder='Enter new allowed email domain'
|
||||||
value={restrictedDomainInput}
|
value={restrictedDomainInput}
|
||||||
onChange={(e, { value }) => {
|
onChange={(e, { value }) => {
|
||||||
setRestrictedDomainInput(value);
|
setRestrictedDomainInput(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={submitEmailDomainWhitelist}>保存邮箱域名白名单Settings</Form.Button>
|
<Form.Button onClick={submitEmailDomainWhitelist}>Save Email Domain Whitelist Settings</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
Configure SMTP
|
Configure SMTP
|
||||||
@ -428,7 +428,7 @@ const SystemSetting = () => {
|
|||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Group widths={3}>
|
<Form.Group widths={3}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='SMTP Sender email'
|
label='SMTP Sender Email'
|
||||||
name='SMTPFrom'
|
name='SMTPFrom'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -450,7 +450,7 @@ const SystemSetting = () => {
|
|||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
Configure GitHub OAuth App
|
Configure GitHub OAuth App
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
To support login & registration via GitHub,
|
To support login & registration via GitHub,
|
||||||
<a href='https://github.com/settings/developers' target='_blank'>
|
<a href='https://github.com/settings/developers' target='_blank'>
|
||||||
Click here
|
Click here
|
||||||
</a>
|
</a>
|
||||||
@ -459,7 +459,7 @@ const SystemSetting = () => {
|
|||||||
</Header>
|
</Header>
|
||||||
<Message>
|
<Message>
|
||||||
Fill in the Homepage URL <code>{inputs.ServerAddress}</code>
|
Fill in the Homepage URL <code>{inputs.ServerAddress}</code>
|
||||||
,Fill in the Authorization callback URL{' '}
|
, Fill in the Authorization callback URL{' '}
|
||||||
<code>{`${inputs.ServerAddress}/oauth/github`}</code>
|
<code>{`${inputs.ServerAddress}/oauth/github`}</code>
|
||||||
</Message>
|
</Message>
|
||||||
<Form.Group widths={3}>
|
<Form.Group widths={3}>
|
||||||
@ -486,18 +486,18 @@ const SystemSetting = () => {
|
|||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
配置飞书授权Log in
|
Configure Lark OAuth
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
用以支持通过飞书进行Log inSign up,
|
To support login & registration via Lark,
|
||||||
<a href='https://open.feishu.cn/app' target='_blank'>
|
<a href='https://open.feishu.cn/app' target='_blank'>
|
||||||
Click here
|
Click here
|
||||||
</a>
|
</a>
|
||||||
Management你的飞书应用
|
Manage your Lark App
|
||||||
</Header.Subheader>
|
</Header.Subheader>
|
||||||
</Header>
|
</Header>
|
||||||
<Message>
|
<Message>
|
||||||
主页链接填 <code>{inputs.ServerAddress}</code>
|
Fill in the Homepage URL <code>{inputs.ServerAddress}</code>
|
||||||
,重定向 URL 填{' '}
|
, Fill in the Redirect URL{' '}
|
||||||
<code>{`${inputs.ServerAddress}/oauth/lark`}</code>
|
<code>{`${inputs.ServerAddress}/oauth/lark`}</code>
|
||||||
</Message>
|
</Message>
|
||||||
<Form.Group widths={3}>
|
<Form.Group widths={3}>
|
||||||
@ -520,13 +520,13 @@ const SystemSetting = () => {
|
|||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={submitLarkOAuth}>
|
<Form.Button onClick={submitLarkOAuth}>
|
||||||
保存飞书 OAuth Settings
|
Save Lark OAuth Settings
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
Configure WeChat Server
|
Configure WeChat Server
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
To support login & registration via WeChat,
|
To support login & registration via WeChat,
|
||||||
<a
|
<a
|
||||||
href='https://github.com/songquanpeng/wechat-server'
|
href='https://github.com/songquanpeng/wechat-server'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
@ -538,9 +538,9 @@ const SystemSetting = () => {
|
|||||||
</Header>
|
</Header>
|
||||||
<Form.Group widths={3}>
|
<Form.Group widths={3}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='WeChat Server Server Address'
|
label='WeChat Server Address'
|
||||||
name='WeChatServerAddress'
|
name='WeChatServerAddress'
|
||||||
placeholder='For example:https://yourdomain.com'
|
placeholder='For example: https://yourdomain.com'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
value={inputs.WeChatServerAddress}
|
value={inputs.WeChatServerAddress}
|
||||||
@ -568,29 +568,29 @@ const SystemSetting = () => {
|
|||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
配置 Message Pusher
|
Configure Message Pusher
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
用以推送报警信息,
|
To push alert messages,
|
||||||
<a
|
<a
|
||||||
href='https://github.com/songquanpeng/message-pusher'
|
href='https://github.com/songquanpeng/message-pusher'
|
||||||
target='_blank'
|
target='_blank'
|
||||||
>
|
>
|
||||||
Click here
|
Click here
|
||||||
</a>
|
</a>
|
||||||
了解 Message Pusher
|
Learn about Message Pusher
|
||||||
</Header.Subheader>
|
</Header.Subheader>
|
||||||
</Header>
|
</Header>
|
||||||
<Form.Group widths={3}>
|
<Form.Group widths={3}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Message Pusher 推送地址'
|
label='Message Pusher Address'
|
||||||
name='MessagePusherAddress'
|
name='MessagePusherAddress'
|
||||||
placeholder='For example:https://msgpusher.com/push/your_username'
|
placeholder='For example: https://msgpusher.com/push/your_username'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
value={inputs.MessagePusherAddress}
|
value={inputs.MessagePusherAddress}
|
||||||
/>
|
/>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Message Pusher 访问凭证'
|
label='Message Pusher Access Credential'
|
||||||
name='MessagePusherToken'
|
name='MessagePusherToken'
|
||||||
type='password'
|
type='password'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
@ -600,13 +600,13 @@ const SystemSetting = () => {
|
|||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={submitMessagePusher}>
|
<Form.Button onClick={submitMessagePusher}>
|
||||||
保存 Message Pusher Settings
|
Save Message Pusher Settings
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
Configure Turnstile
|
Configure Turnstile
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
To support user verification,
|
To support user verification,
|
||||||
<a href='https://dash.cloudflare.com/' target='_blank'>
|
<a href='https://dash.cloudflare.com/' target='_blank'>
|
||||||
Click here
|
Click here
|
||||||
</a>
|
</a>
|
||||||
|
@ -422,12 +422,12 @@ const TokensTable = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button size='small' onClick={refresh} loading={loading}>Refresh</Button>
|
<Button size='small' onClick={refresh} loading={loading}>Refresh</Button>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
placeholder='排序方式'
|
placeholder='Sort By'
|
||||||
selection
|
selection
|
||||||
options={[
|
options={[
|
||||||
{ key: '', text: 'Default排序', value: '' },
|
{ key: '', text: 'Default Order', value: '' },
|
||||||
{ key: 'remain_quota', text: '按Remaining quota排序', value: 'remain_quota' },
|
{ key: 'remain_quota', text: 'Sort by Remaining Quota', value: 'remain_quota' },
|
||||||
{ key: 'used_quota', text: '按Used quota排序', value: 'used_quota' },
|
{ key: 'used_quota', text: 'Sort by Used Quota', value: 'used_quota' },
|
||||||
]}
|
]}
|
||||||
value={orderBy}
|
value={orderBy}
|
||||||
onChange={handleOrderByChange}
|
onChange={handleOrderByChange}
|
||||||
|
@ -330,13 +330,13 @@ const UsersTable = () => {
|
|||||||
Add New User
|
Add New User
|
||||||
</Button>
|
</Button>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
placeholder='排序方式'
|
placeholder='Sort By'
|
||||||
selection
|
selection
|
||||||
options={[
|
options={[
|
||||||
{ key: '', text: 'Default排序', value: '' },
|
{ key: '', text: 'Default Order', value: '' },
|
||||||
{ key: 'quota', text: '按Remaining quota排序', value: 'quota' },
|
{ key: 'quota', text: 'Sort by Remaining Quota', value: 'quota' },
|
||||||
{ key: 'used_quota', text: '按Used quota排序', value: 'used_quota' },
|
{ key: 'used_quota', text: 'Sort by Used Quota', value: 'used_quota' },
|
||||||
{ key: 'request_count', text: '按Number of Requests排序', value: 'request_count' },
|
{ key: 'request_count', text: 'Sort by Number of Requests', value: 'request_count' },
|
||||||
]}
|
]}
|
||||||
value={orderBy}
|
value={orderBy}
|
||||||
onChange={handleOrderByChange}
|
onChange={handleOrderByChange}
|
||||||
|
@ -138,8 +138,8 @@ const EditToken = () => {
|
|||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Dropdown
|
<Form.Dropdown
|
||||||
label='Model范围'
|
label='Model Range'
|
||||||
placeholder={'请选择允许使用的Model,留空则不进行限制'}
|
placeholder={'Please select the allowed models, leave blank for no restriction'}
|
||||||
name='models'
|
name='models'
|
||||||
fluid
|
fluid
|
||||||
multiple
|
multiple
|
||||||
@ -156,9 +156,9 @@ const EditToken = () => {
|
|||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='IP 限制'
|
label='IP Restriction'
|
||||||
name='subnet'
|
name='subnet'
|
||||||
placeholder={'请Enter允许访问的网段,For example:192.168.0.0/24,请使用英文逗号分隔多个网段'}
|
placeholder={'Please enter the allowed subnet, e.g., 192.168.0.0/24, use commas to separate multiple subnets'}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={inputs.subnet}
|
value={inputs.subnet}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -166,9 +166,9 @@ const EditToken = () => {
|
|||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Expiration time'
|
label='Expiration Time'
|
||||||
name='expired_time'
|
name='expired_time'
|
||||||
placeholder={'Please enter the expiration time, the format is yyyy-MM-dd HH:mm:ss, -1 means unlimited'}
|
placeholder={'Please enter the expiration time, format: yyyy-MM-dd HH:mm:ss, -1 means unlimited'}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
value={expired_time}
|
value={expired_time}
|
||||||
autoComplete='new-password'
|
autoComplete='new-password'
|
||||||
@ -192,7 +192,7 @@ const EditToken = () => {
|
|||||||
setExpiredTime(0, 0, 0, 1);
|
setExpiredTime(0, 0, 0, 1);
|
||||||
}}>Expires after one minute</Button>
|
}}>Expires after one minute</Button>
|
||||||
</div>
|
</div>
|
||||||
<Message>Note that the quota of the token is only used to limit the maximum quota usage of the token itself, and the actual usage is limited by the remaining quota of the account.</Message>
|
<Message>Note that the token's quota is only used to limit the maximum usage of the token itself, and the actual usage is limited by the remaining quota of the account.</Message>
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label={`Quota${renderQuotaWithPrompt(remain_quota)}`}
|
label={`Quota${renderQuotaWithPrompt(remain_quota)}`}
|
||||||
|
Loading…
Reference in New Issue
Block a user