mirror of
https://github.com/linux-do/new-api.git
synced 2025-09-17 16:06:38 +08:00
Merge branch 'main' into telegram-login
This commit is contained in:
commit
02d5a5f16d
@ -9,14 +9,19 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Pay Settings
|
||||||
|
|
||||||
|
var PayAddress = ""
|
||||||
|
var CustomCallbackAddress = ""
|
||||||
|
var EpayId = ""
|
||||||
|
var EpayKey = ""
|
||||||
|
var Price = 7.3
|
||||||
|
var MinTopUp = 1
|
||||||
|
|
||||||
var StartTime = time.Now().Unix() // unit: second
|
var StartTime = time.Now().Unix() // unit: second
|
||||||
var Version = "v0.0.0" // this hard coding will be replaced automatically when building, no need to manually change
|
var Version = "v0.0.0" // this hard coding will be replaced automatically when building, no need to manually change
|
||||||
var SystemName = "New API"
|
var SystemName = "New API"
|
||||||
var ServerAddress = "http://localhost:3000"
|
var ServerAddress = "http://localhost:3000"
|
||||||
var PayAddress = ""
|
|
||||||
var EpayId = ""
|
|
||||||
var EpayKey = ""
|
|
||||||
var Price = 7.3
|
|
||||||
var Footer = ""
|
var Footer = ""
|
||||||
var Logo = ""
|
var Logo = ""
|
||||||
var TopUpLink = ""
|
var TopUpLink = ""
|
||||||
@ -29,6 +34,7 @@ var DrawingEnabled = true
|
|||||||
var DataExportEnabled = true
|
var DataExportEnabled = true
|
||||||
var DataExportInterval = 5 // unit: minute
|
var DataExportInterval = 5 // unit: minute
|
||||||
var DataExportDefaultTime = "hour" // unit: minute
|
var DataExportDefaultTime = "hour" // unit: minute
|
||||||
|
var DefaultCollapseSidebar = false // default value of collapse sidebar
|
||||||
|
|
||||||
// Any options with "Secret", "Token" in its key won't be return by GetOptions
|
// Any options with "Secret", "Token" in its key won't be return by GetOptions
|
||||||
|
|
||||||
|
@ -80,7 +80,10 @@ var ModelRatio = map[string]float64{
|
|||||||
"qwen-turbo": 0.8572, // ¥0.012 / 1k tokens
|
"qwen-turbo": 0.8572, // ¥0.012 / 1k tokens
|
||||||
"qwen-plus": 10, // ¥0.14 / 1k tokens
|
"qwen-plus": 10, // ¥0.14 / 1k tokens
|
||||||
"text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens
|
"text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens
|
||||||
"SparkDesk": 1.2858, // ¥0.018 / 1k tokens
|
"SparkDesk-v1.1": 1.2858, // ¥0.018 / 1k tokens
|
||||||
|
"SparkDesk-v2.1": 1.2858, // ¥0.018 / 1k tokens
|
||||||
|
"SparkDesk-v3.1": 1.2858, // ¥0.018 / 1k tokens
|
||||||
|
"SparkDesk-v3.5": 1.2858, // ¥0.018 / 1k tokens
|
||||||
"360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens
|
"360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens
|
||||||
"embedding-bert-512-v1": 0.0715, // ¥0.001 / 1k tokens
|
"embedding-bert-512-v1": 0.0715, // ¥0.001 / 1k tokens
|
||||||
"embedding_s1_v1": 0.0715, // ¥0.001 / 1k tokens
|
"embedding_s1_v1": 0.0715, // ¥0.001 / 1k tokens
|
||||||
|
@ -54,8 +54,9 @@ func FixChannelsAbilities(c *gin.Context) {
|
|||||||
func SearchChannels(c *gin.Context) {
|
func SearchChannels(c *gin.Context) {
|
||||||
keyword := c.Query("keyword")
|
keyword := c.Query("keyword")
|
||||||
group := c.Query("group")
|
group := c.Query("group")
|
||||||
|
modelKeyword := c.Query("model")
|
||||||
//idSort, _ := strconv.ParseBool(c.Query("id_sort"))
|
//idSort, _ := strconv.ParseBool(c.Query("id_sort"))
|
||||||
channels, err := model.SearchChannels(keyword, group)
|
channels, err := model.SearchChannels(keyword, group, modelKeyword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": false,
|
"success": false,
|
||||||
|
@ -29,6 +29,7 @@ func GetStatus(c *gin.Context) {
|
|||||||
"wechat_login": common.WeChatAuthEnabled,
|
"wechat_login": common.WeChatAuthEnabled,
|
||||||
"server_address": common.ServerAddress,
|
"server_address": common.ServerAddress,
|
||||||
"price": common.Price,
|
"price": common.Price,
|
||||||
|
"min_topup": common.MinTopUp,
|
||||||
"turnstile_check": common.TurnstileCheckEnabled,
|
"turnstile_check": common.TurnstileCheckEnabled,
|
||||||
"turnstile_site_key": common.TurnstileSiteKey,
|
"turnstile_site_key": common.TurnstileSiteKey,
|
||||||
"top_up_link": common.TopUpLink,
|
"top_up_link": common.TopUpLink,
|
||||||
@ -40,6 +41,7 @@ func GetStatus(c *gin.Context) {
|
|||||||
"enable_drawing": common.DrawingEnabled,
|
"enable_drawing": common.DrawingEnabled,
|
||||||
"enable_data_export": common.DataExportEnabled,
|
"enable_data_export": common.DataExportEnabled,
|
||||||
"data_export_default_time": common.DataExportDefaultTime,
|
"data_export_default_time": common.DataExportDefaultTime,
|
||||||
|
"default_collapse_sidebar": common.DefaultCollapseSidebar,
|
||||||
"enable_online_topup": common.PayAddress != "" && common.EpayId != "" && common.EpayKey != "",
|
"enable_online_topup": common.PayAddress != "" && common.EpayId != "" && common.EpayKey != "",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -129,6 +129,13 @@ func ListModels(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ChannelListModels(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"object": "list",
|
||||||
|
"data": openAIModels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func RetrieveModel(c *gin.Context) {
|
func RetrieveModel(c *gin.Context) {
|
||||||
modelId := c.Param("model")
|
modelId := c.Param("model")
|
||||||
if model, ok := openAIModelsMap[modelId]; ok {
|
if model, ok := openAIModelsMap[modelId]; ok {
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"one-api/common"
|
"one-api/common"
|
||||||
"one-api/model"
|
"one-api/model"
|
||||||
|
"one-api/service"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -55,14 +56,14 @@ func RequestEpay(c *gin.Context) {
|
|||||||
c.JSON(200, gin.H{"message": err.Error(), "data": 10})
|
c.JSON(200, gin.H{"message": err.Error(), "data": 10})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if req.Amount < 1 {
|
if req.Amount < common.MinTopUp {
|
||||||
c.JSON(200, gin.H{"message": "充值金额不能小于1", "data": 10})
|
c.JSON(200, gin.H{"message": fmt.Sprintf("充值数量不能小于 %d", common.MinTopUp), "data": 10})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
id := c.GetInt("id")
|
id := c.GetInt("id")
|
||||||
user, _ := model.GetUserById(id, false)
|
user, _ := model.GetUserById(id, false)
|
||||||
amount := GetAmount(float64(req.Amount), *user)
|
payMoney := GetAmount(float64(req.Amount), *user)
|
||||||
|
|
||||||
var payType epay.PurchaseType
|
var payType epay.PurchaseType
|
||||||
if req.PaymentMethod == "zfb" {
|
if req.PaymentMethod == "zfb" {
|
||||||
@ -72,11 +73,10 @@ func RequestEpay(c *gin.Context) {
|
|||||||
req.PaymentMethod = "wxpay"
|
req.PaymentMethod = "wxpay"
|
||||||
payType = epay.WechatPay
|
payType = epay.WechatPay
|
||||||
}
|
}
|
||||||
|
callBackAddress := service.GetCallbackAddress()
|
||||||
returnUrl, _ := url.Parse(common.ServerAddress + "/log")
|
returnUrl, _ := url.Parse(common.ServerAddress + "/log")
|
||||||
notifyUrl, _ := url.Parse(common.ServerAddress + "/api/user/epay/notify")
|
notifyUrl, _ := url.Parse(callBackAddress + "/api/user/epay/notify")
|
||||||
tradeNo := strconv.FormatInt(time.Now().Unix(), 10)
|
tradeNo := strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
payMoney := amount
|
|
||||||
client := GetEpayClient()
|
client := GetEpayClient()
|
||||||
if client == nil {
|
if client == nil {
|
||||||
c.JSON(200, gin.H{"message": "error", "data": "当前管理员未配置支付信息"})
|
c.JSON(200, gin.H{"message": "error", "data": "当前管理员未配置支付信息"})
|
||||||
@ -169,8 +169,8 @@ func RequestAmount(c *gin.Context) {
|
|||||||
c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
|
c.JSON(200, gin.H{"message": "error", "data": "参数错误"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if req.Amount < 1 {
|
if req.Amount < common.MinTopUp {
|
||||||
c.JSON(200, gin.H{"message": "error", "data": "充值金额不能小于1"})
|
c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", common.MinTopUp)})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := c.GetInt("id")
|
id := c.GetInt("id")
|
||||||
|
@ -291,24 +291,27 @@ func CacheGetRandomSatisfiedChannel(group string, model string) (*Channel, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 平滑系数
|
||||||
|
smoothingFactor := 10
|
||||||
// Calculate the total weight of all channels up to endIdx
|
// Calculate the total weight of all channels up to endIdx
|
||||||
totalWeight := 0
|
totalWeight := 0
|
||||||
for _, channel := range channels[:endIdx] {
|
for _, channel := range channels[:endIdx] {
|
||||||
totalWeight += channel.GetWeight()
|
totalWeight += channel.GetWeight() + smoothingFactor
|
||||||
}
|
}
|
||||||
|
|
||||||
if totalWeight == 0 {
|
//if totalWeight == 0 {
|
||||||
// If all weights are 0, select a channel randomly
|
// // If all weights are 0, select a channel randomly
|
||||||
return channels[rand.Intn(endIdx)], nil
|
// return channels[rand.Intn(endIdx)], nil
|
||||||
}
|
//}
|
||||||
|
|
||||||
// Generate a random value in the range [0, totalWeight)
|
// Generate a random value in the range [0, totalWeight)
|
||||||
randomWeight := rand.Intn(totalWeight)
|
randomWeight := rand.Intn(totalWeight)
|
||||||
|
|
||||||
// Find a channel based on its weight
|
// Find a channel based on its weight
|
||||||
for _, channel := range channels[:endIdx] {
|
for _, channel := range channels[:endIdx] {
|
||||||
randomWeight -= channel.GetWeight()
|
randomWeight -= channel.GetWeight() + smoothingFactor
|
||||||
if randomWeight <= 0 {
|
if randomWeight < 0 {
|
||||||
return channel, nil
|
return channel, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,21 +43,39 @@ func GetAllChannels(startIdx int, num int, selectAll bool, idSort bool) ([]*Chan
|
|||||||
return channels, err
|
return channels, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchChannels(keyword string, group string) (channels []*Channel, err error) {
|
func SearchChannels(keyword string, group string, model string) ([]*Channel, error) {
|
||||||
|
var channels []*Channel
|
||||||
keyCol := "`key`"
|
keyCol := "`key`"
|
||||||
|
groupCol := "`group`"
|
||||||
|
modelsCol := "`models`"
|
||||||
|
|
||||||
|
// 如果是 PostgreSQL,使用双引号
|
||||||
if common.UsingPostgreSQL {
|
if common.UsingPostgreSQL {
|
||||||
keyCol = `"key"`
|
keyCol = `"key"`
|
||||||
|
groupCol = `"group"`
|
||||||
|
modelsCol = `"models"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 构造基础查询
|
||||||
|
baseQuery := DB.Model(&Channel{}).Omit(keyCol)
|
||||||
|
|
||||||
|
// 构造WHERE子句
|
||||||
|
var whereClause string
|
||||||
|
var args []interface{}
|
||||||
if group != "" {
|
if group != "" {
|
||||||
groupCol := "`group`"
|
whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + groupCol + " LIKE ? AND " + modelsCol + " LIKE ?"
|
||||||
if common.UsingPostgreSQL {
|
args = append(args, common.String2Int(keyword), "%"+keyword+"%", keyword, "%"+group+"%", "%"+model+"%")
|
||||||
groupCol = `"group"`
|
|
||||||
}
|
|
||||||
err = DB.Omit("key").Where("(id = ? or name LIKE ? or "+keyCol+" = ?) and "+groupCol+" LIKE ?", common.String2Int(keyword), keyword+"%", keyword, "%"+group+"%").Find(&channels).Error
|
|
||||||
} else {
|
} else {
|
||||||
err = DB.Omit("key").Where("id = ? or name LIKE ? or "+keyCol+" = ?", common.String2Int(keyword), keyword+"%", keyword).Find(&channels).Error
|
whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + modelsCol + " LIKE ?"
|
||||||
|
args = append(args, common.String2Int(keyword), "%"+keyword+"%", keyword, "%"+model+"%")
|
||||||
}
|
}
|
||||||
return channels, err
|
|
||||||
|
// 执行查询
|
||||||
|
err := baseQuery.Where(whereClause, args...).Find(&channels).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return channels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetChannelById(id int, selectAll bool) (*Channel, error) {
|
func GetChannelById(id int, selectAll bool) (*Channel, error) {
|
||||||
|
@ -57,9 +57,11 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["Logo"] = common.Logo
|
common.OptionMap["Logo"] = common.Logo
|
||||||
common.OptionMap["ServerAddress"] = ""
|
common.OptionMap["ServerAddress"] = ""
|
||||||
common.OptionMap["PayAddress"] = ""
|
common.OptionMap["PayAddress"] = ""
|
||||||
|
common.OptionMap["CustomCallbackAddress"] = ""
|
||||||
common.OptionMap["EpayId"] = ""
|
common.OptionMap["EpayId"] = ""
|
||||||
common.OptionMap["EpayKey"] = ""
|
common.OptionMap["EpayKey"] = ""
|
||||||
common.OptionMap["Price"] = strconv.FormatFloat(common.Price, 'f', -1, 64)
|
common.OptionMap["Price"] = strconv.FormatFloat(common.Price, 'f', -1, 64)
|
||||||
|
common.OptionMap["MinTopUp"] = strconv.Itoa(common.MinTopUp)
|
||||||
common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString()
|
common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString()
|
||||||
common.OptionMap["GitHubClientId"] = ""
|
common.OptionMap["GitHubClientId"] = ""
|
||||||
common.OptionMap["GitHubClientSecret"] = ""
|
common.OptionMap["GitHubClientSecret"] = ""
|
||||||
@ -85,6 +87,7 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["RetryTimes"] = strconv.Itoa(common.RetryTimes)
|
common.OptionMap["RetryTimes"] = strconv.Itoa(common.RetryTimes)
|
||||||
common.OptionMap["DataExportInterval"] = strconv.Itoa(common.DataExportInterval)
|
common.OptionMap["DataExportInterval"] = strconv.Itoa(common.DataExportInterval)
|
||||||
common.OptionMap["DataExportDefaultTime"] = common.DataExportDefaultTime
|
common.OptionMap["DataExportDefaultTime"] = common.DataExportDefaultTime
|
||||||
|
common.OptionMap["DefaultCollapseSidebar"] = strconv.FormatBool(common.DefaultCollapseSidebar)
|
||||||
|
|
||||||
common.OptionMapRWMutex.Unlock()
|
common.OptionMapRWMutex.Unlock()
|
||||||
loadOptionsFromDatabase()
|
loadOptionsFromDatabase()
|
||||||
@ -141,7 +144,7 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
common.ImageDownloadPermission = intValue
|
common.ImageDownloadPermission = intValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasSuffix(key, "Enabled") {
|
if strings.HasSuffix(key, "Enabled") || key == "DefaultCollapseSidebar" {
|
||||||
boolValue := value == "true"
|
boolValue := value == "true"
|
||||||
switch key {
|
switch key {
|
||||||
case "PasswordRegisterEnabled":
|
case "PasswordRegisterEnabled":
|
||||||
@ -176,6 +179,8 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
common.DrawingEnabled = boolValue
|
common.DrawingEnabled = boolValue
|
||||||
case "DataExportEnabled":
|
case "DataExportEnabled":
|
||||||
common.DataExportEnabled = boolValue
|
common.DataExportEnabled = boolValue
|
||||||
|
case "DefaultCollapseSidebar":
|
||||||
|
common.DefaultCollapseSidebar = boolValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch key {
|
switch key {
|
||||||
@ -196,12 +201,16 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
common.ServerAddress = value
|
common.ServerAddress = value
|
||||||
case "PayAddress":
|
case "PayAddress":
|
||||||
common.PayAddress = value
|
common.PayAddress = value
|
||||||
|
case "CustomCallbackAddress":
|
||||||
|
common.CustomCallbackAddress = value
|
||||||
case "EpayId":
|
case "EpayId":
|
||||||
common.EpayId = value
|
common.EpayId = value
|
||||||
case "EpayKey":
|
case "EpayKey":
|
||||||
common.EpayKey = value
|
common.EpayKey = value
|
||||||
case "Price":
|
case "Price":
|
||||||
common.Price, _ = strconv.ParseFloat(value, 64)
|
common.Price, _ = strconv.ParseFloat(value, 64)
|
||||||
|
case "MinTopUp":
|
||||||
|
common.MinTopUp, _ = strconv.Atoi(value)
|
||||||
case "TopupGroupRatio":
|
case "TopupGroupRatio":
|
||||||
err = common.UpdateTopupGroupRatioByJSONString(value)
|
err = common.UpdateTopupGroupRatioByJSONString(value)
|
||||||
case "GitHubClientId":
|
case "GitHubClientId":
|
||||||
|
@ -71,10 +71,10 @@ func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, request
|
|||||||
func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage *dto.Usage, err *dto.OpenAIErrorWithStatusCode) {
|
func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage *dto.Usage, err *dto.OpenAIErrorWithStatusCode) {
|
||||||
if info.IsStream {
|
if info.IsStream {
|
||||||
var responseText string
|
var responseText string
|
||||||
err, responseText = openaiStreamHandler(c, resp, info.RelayMode)
|
err, responseText = OpenaiStreamHandler(c, resp, info.RelayMode)
|
||||||
usage = service.ResponseText2Usage(responseText, info.UpstreamModelName, info.PromptTokens)
|
usage = service.ResponseText2Usage(responseText, info.UpstreamModelName, info.PromptTokens)
|
||||||
} else {
|
} else {
|
||||||
err, usage = openaiHandler(c, resp, info.PromptTokens, info.UpstreamModelName)
|
err, usage = OpenaiHandler(c, resp, info.PromptTokens, info.UpstreamModelName)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func openaiStreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*dto.OpenAIErrorWithStatusCode, string) {
|
func OpenaiStreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*dto.OpenAIErrorWithStatusCode, string) {
|
||||||
var responseTextBuilder strings.Builder
|
var responseTextBuilder strings.Builder
|
||||||
scanner := bufio.NewScanner(resp.Body)
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
@ -111,7 +111,7 @@ func openaiStreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*d
|
|||||||
return nil, responseTextBuilder.String()
|
return nil, responseTextBuilder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func openaiHandler(c *gin.Context, resp *http.Response, promptTokens int, model string) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) {
|
func OpenaiHandler(c *gin.Context, resp *http.Response, promptTokens int, model string) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) {
|
||||||
var textResponse dto.TextResponse
|
var textResponse dto.TextResponse
|
||||||
responseBody, err := io.ReadAll(resp.Body)
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package zhipu_v4
|
package zhipu_4v
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -8,7 +8,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"one-api/dto"
|
"one-api/dto"
|
||||||
"one-api/relay/channel"
|
"one-api/relay/channel"
|
||||||
|
"one-api/relay/channel/openai"
|
||||||
relaycommon "one-api/relay/common"
|
relaycommon "one-api/relay/common"
|
||||||
|
"one-api/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Adaptor struct {
|
type Adaptor struct {
|
||||||
@ -41,9 +43,11 @@ func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, request
|
|||||||
|
|
||||||
func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage *dto.Usage, err *dto.OpenAIErrorWithStatusCode) {
|
func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage *dto.Usage, err *dto.OpenAIErrorWithStatusCode) {
|
||||||
if info.IsStream {
|
if info.IsStream {
|
||||||
err, usage = zhipuStreamHandler(c, resp)
|
var responseText string
|
||||||
|
err, responseText = openai.OpenaiStreamHandler(c, resp, info.RelayMode)
|
||||||
|
usage = service.ResponseText2Usage(responseText, info.UpstreamModelName, info.PromptTokens)
|
||||||
} else {
|
} else {
|
||||||
err, usage = zhipuHandler(c, resp)
|
err, usage = openai.OpenaiHandler(c, resp, info.PromptTokens, info.UpstreamModelName)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package zhipu_v4
|
package zhipu_4v
|
||||||
|
|
||||||
var ModelList = []string{
|
var ModelList = []string{
|
||||||
"glm-4", "glm-4v", "glm-3-turbo",
|
"glm-4", "glm-4v", "glm-3-turbo",
|
||||||
}
|
}
|
||||||
|
|
||||||
var ChannelName = "zhipu_v4"
|
var ChannelName = "zhipu_4v"
|
@ -1,4 +1,4 @@
|
|||||||
package zhipu_v4
|
package zhipu_4v
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"one-api/dto"
|
"one-api/dto"
|
@ -1,4 +1,4 @@
|
|||||||
package zhipu_v4
|
package zhipu_4v
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
@ -59,6 +59,7 @@ func getAndValidateTextRequest(c *gin.Context, relayInfo *relaycommon.RelayInfo)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
relayInfo.IsStream = textRequest.Stream
|
relayInfo.IsStream = textRequest.Stream
|
||||||
|
relayInfo.UpstreamModelName = textRequest.Model
|
||||||
return textRequest, nil
|
return textRequest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"one-api/relay/channel/tencent"
|
"one-api/relay/channel/tencent"
|
||||||
"one-api/relay/channel/xunfei"
|
"one-api/relay/channel/xunfei"
|
||||||
"one-api/relay/channel/zhipu"
|
"one-api/relay/channel/zhipu"
|
||||||
"one-api/relay/channel/zhipu_v4"
|
"one-api/relay/channel/zhipu_4v"
|
||||||
"one-api/relay/constant"
|
"one-api/relay/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ func GetAdaptor(apiType int) channel.Adaptor {
|
|||||||
case constant.APITypeZhipu:
|
case constant.APITypeZhipu:
|
||||||
return &zhipu.Adaptor{}
|
return &zhipu.Adaptor{}
|
||||||
case constant.APITypeZhipu_v4:
|
case constant.APITypeZhipu_v4:
|
||||||
return &zhipu_v4.Adaptor{}
|
return &zhipu_4v.Adaptor{}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ func SetApiRouter(router *gin.Engine) {
|
|||||||
{
|
{
|
||||||
channelRoute.GET("/", controller.GetAllChannels)
|
channelRoute.GET("/", controller.GetAllChannels)
|
||||||
channelRoute.GET("/search", controller.SearchChannels)
|
channelRoute.GET("/search", controller.SearchChannels)
|
||||||
channelRoute.GET("/models", controller.ListModels)
|
channelRoute.GET("/models", controller.ChannelListModels)
|
||||||
channelRoute.GET("/:id", controller.GetChannel)
|
channelRoute.GET("/:id", controller.GetChannel)
|
||||||
channelRoute.GET("/test", controller.TestAllChannels)
|
channelRoute.GET("/test", controller.TestAllChannels)
|
||||||
channelRoute.GET("/test/:id", controller.TestChannel)
|
channelRoute.GET("/test/:id", controller.TestChannel)
|
||||||
|
10
service/epay.go
Normal file
10
service/epay.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import "one-api/common"
|
||||||
|
|
||||||
|
func GetCallbackAddress() string {
|
||||||
|
if common.CustomCallbackAddress == "" {
|
||||||
|
return common.ServerAddress
|
||||||
|
}
|
||||||
|
return common.CustomCallbackAddress
|
||||||
|
}
|
@ -29,7 +29,7 @@ const Home = lazy(() => import('./pages/Home'));
|
|||||||
const About = lazy(() => import('./pages/About'));
|
const About = lazy(() => import('./pages/About'));
|
||||||
function App() {
|
function App() {
|
||||||
const [userState, userDispatch] = useContext(UserContext);
|
const [userState, userDispatch] = useContext(UserContext);
|
||||||
const [statusState, statusDispatch] = useContext(StatusContext);
|
// const [statusState, statusDispatch] = useContext(StatusContext);
|
||||||
|
|
||||||
const loadUser = () => {
|
const loadUser = () => {
|
||||||
let user = localStorage.getItem('user');
|
let user = localStorage.getItem('user');
|
||||||
@ -38,47 +38,9 @@ function App() {
|
|||||||
userDispatch({ type: 'login', payload: data });
|
userDispatch({ type: 'login', payload: data });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const loadStatus = async () => {
|
|
||||||
const res = await API.get('/api/status');
|
|
||||||
const { success, data } = res.data;
|
|
||||||
if (success) {
|
|
||||||
localStorage.setItem('status', JSON.stringify(data));
|
|
||||||
statusDispatch({ type: 'set', payload: data });
|
|
||||||
localStorage.setItem('system_name', data.system_name);
|
|
||||||
localStorage.setItem('logo', data.logo);
|
|
||||||
localStorage.setItem('footer_html', data.footer_html);
|
|
||||||
localStorage.setItem('quota_per_unit', data.quota_per_unit);
|
|
||||||
localStorage.setItem('display_in_currency', data.display_in_currency);
|
|
||||||
localStorage.setItem('enable_drawing', data.enable_drawing);
|
|
||||||
localStorage.setItem('enable_data_export', data.enable_data_export);
|
|
||||||
localStorage.setItem('data_export_default_time', data.data_export_default_time);
|
|
||||||
if (data.chat_link) {
|
|
||||||
localStorage.setItem('chat_link', data.chat_link);
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem('chat_link');
|
|
||||||
}
|
|
||||||
if (data.chat_link2) {
|
|
||||||
localStorage.setItem('chat_link2', data.chat_link2);
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem('chat_link2');
|
|
||||||
}
|
|
||||||
// if (
|
|
||||||
// data.version !== process.env.REACT_APP_VERSION &&
|
|
||||||
// data.version !== 'v0.0.0' &&
|
|
||||||
// process.env.REACT_APP_VERSION !== ''
|
|
||||||
// ) {
|
|
||||||
// showNotice(
|
|
||||||
// `新版本可用:${data.version},请使用快捷键 Shift + F5 刷新页面`
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
} else {
|
|
||||||
showError('无法正常连接至服务器!');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadUser();
|
loadUser();
|
||||||
loadStatus().then();
|
|
||||||
let systemName = getSystemName();
|
let systemName = getSystemName();
|
||||||
if (systemName) {
|
if (systemName) {
|
||||||
document.title = systemName;
|
document.title = systemName;
|
||||||
|
@ -257,6 +257,7 @@ const ChannelsTable = () => {
|
|||||||
const [idSort, setIdSort] = useState(false);
|
const [idSort, setIdSort] = useState(false);
|
||||||
const [searchKeyword, setSearchKeyword] = useState('');
|
const [searchKeyword, setSearchKeyword] = useState('');
|
||||||
const [searchGroup, setSearchGroup] = useState('');
|
const [searchGroup, setSearchGroup] = useState('');
|
||||||
|
const [searchModel, setSearchModel] = useState('');
|
||||||
const [searching, setSearching] = useState(false);
|
const [searching, setSearching] = useState(false);
|
||||||
const [updatingBalance, setUpdatingBalance] = useState(false);
|
const [updatingBalance, setUpdatingBalance] = useState(false);
|
||||||
const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE);
|
const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE);
|
||||||
@ -440,15 +441,15 @@ const ChannelsTable = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const searchChannels = async (searchKeyword, searchGroup) => {
|
const searchChannels = async (searchKeyword, searchGroup, searchModel) => {
|
||||||
if (searchKeyword === '' && searchGroup === '') {
|
if (searchKeyword === '' && searchGroup === '' && searchModel === '') {
|
||||||
// if keyword is blank, load files instead.
|
// if keyword is blank, load files instead.
|
||||||
await loadChannels(0, pageSize, idSort);
|
await loadChannels(0, pageSize, idSort);
|
||||||
setActivePage(1);
|
setActivePage(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSearching(true);
|
setSearching(true);
|
||||||
const res = await API.get(`/api/channel/search?keyword=${searchKeyword}&group=${searchGroup}`);
|
const res = await API.get(`/api/channel/search?keyword=${searchKeyword}&group=${searchGroup}&model=${searchModel}`);
|
||||||
const {success, message, data} = res.data;
|
const {success, message, data} = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setChannels(data);
|
setChannels(data);
|
||||||
@ -625,13 +626,12 @@ const ChannelsTable = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EditChannel refresh={refresh} visible={showEdit} handleClose={closeEdit} editingChannel={editingChannel}/>
|
<EditChannel refresh={refresh} visible={showEdit} handleClose={closeEdit} editingChannel={editingChannel}/>
|
||||||
<Form onSubmit={() => {searchChannels(searchKeyword, searchGroup)}} labelPosition='left'>
|
<Form onSubmit={() => {searchChannels(searchKeyword, searchGroup, searchModel)}} labelPosition='left'>
|
||||||
|
|
||||||
<div style={{display: 'flex'}}>
|
<div style={{display: 'flex'}}>
|
||||||
<Space>
|
<Space>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
field='search'
|
field='search_keyword'
|
||||||
label='关键词'
|
label='搜索渠道关键词'
|
||||||
placeholder='ID,名称和密钥 ...'
|
placeholder='ID,名称和密钥 ...'
|
||||||
value={searchKeyword}
|
value={searchKeyword}
|
||||||
loading={searching}
|
loading={searching}
|
||||||
@ -639,10 +639,22 @@ const ChannelsTable = () => {
|
|||||||
setSearchKeyword(v.trim())
|
setSearchKeyword(v.trim())
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Form.Input
|
||||||
|
field='search_model'
|
||||||
|
label='模型'
|
||||||
|
placeholder='模型关键字'
|
||||||
|
value={searchModel}
|
||||||
|
loading={searching}
|
||||||
|
onChange={(v)=>{
|
||||||
|
setSearchModel(v.trim())
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Form.Select field="group" label='分组' optionList={groupOptions} onChange={(v) => {
|
<Form.Select field="group" label='分组' optionList={groupOptions} onChange={(v) => {
|
||||||
setSearchGroup(v)
|
setSearchGroup(v)
|
||||||
searchChannels(searchKeyword, v)
|
searchChannels(searchKeyword, v, searchModel)
|
||||||
}}/>
|
}}/>
|
||||||
|
<Button label='查询' type="primary" htmlType="submit" className="btn-margin-right"
|
||||||
|
style={{marginRight: 8}}>查询</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -27,6 +27,7 @@ const OperationSetting = () => {
|
|||||||
DataExportEnabled: '',
|
DataExportEnabled: '',
|
||||||
DataExportDefaultTime: 'hour',
|
DataExportDefaultTime: 'hour',
|
||||||
DataExportInterval: 5,
|
DataExportInterval: 5,
|
||||||
|
DefaultCollapseSidebar: '', // 默认折叠侧边栏
|
||||||
RetryTimes: 0
|
RetryTimes: 0
|
||||||
});
|
});
|
||||||
const [originInputs, setOriginInputs] = useState({});
|
const [originInputs, setOriginInputs] = useState({});
|
||||||
@ -65,6 +66,10 @@ const OperationSetting = () => {
|
|||||||
if (key.endsWith('Enabled')) {
|
if (key.endsWith('Enabled')) {
|
||||||
value = inputs[key] === 'true' ? 'false' : 'true';
|
value = inputs[key] === 'true' ? 'false' : 'true';
|
||||||
}
|
}
|
||||||
|
if (key === 'DefaultCollapseSidebar') {
|
||||||
|
value = inputs[key] === 'true' ? 'false' : 'true';
|
||||||
|
}
|
||||||
|
console.log(key, value)
|
||||||
const res = await API.put('/api/option/', {
|
const res = await API.put('/api/option/', {
|
||||||
key,
|
key,
|
||||||
value
|
value
|
||||||
@ -79,7 +84,7 @@ const OperationSetting = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleInputChange = async (e, {name, value}) => {
|
const handleInputChange = async (e, {name, value}) => {
|
||||||
if (name.endsWith('Enabled') || name === 'DataExportInterval' || name === 'DataExportDefaultTime') {
|
if (name.endsWith('Enabled') || name === 'DataExportInterval' || name === 'DataExportDefaultTime' || name === 'DefaultCollapseSidebar') {
|
||||||
if (name === 'DataExportDefaultTime') {
|
if (name === 'DataExportDefaultTime') {
|
||||||
localStorage.setItem('data_export_default_time', value);
|
localStorage.setItem('data_export_default_time', value);
|
||||||
}
|
}
|
||||||
@ -243,6 +248,12 @@ const OperationSetting = () => {
|
|||||||
name='DrawingEnabled'
|
name='DrawingEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
<Form.Checkbox
|
||||||
|
checked={inputs.DefaultCollapseSidebar === 'true'}
|
||||||
|
label='默认折叠侧边栏'
|
||||||
|
name='DefaultCollapseSidebar'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={() => {
|
<Form.Button onClick={() => {
|
||||||
submitConfig('general').then();
|
submitConfig('general').then();
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, {useContext, useMemo, useState} from 'react';
|
import React, { useContext, useEffect, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import {Link, useNavigate} from 'react-router-dom';
|
import {Link, useNavigate} from 'react-router-dom';
|
||||||
import {UserContext} from '../context/User';
|
import {UserContext} from '../context/User';
|
||||||
|
|
||||||
import {API, getLogo, getSystemName, isAdmin, isMobile, showSuccess} from '../helpers';
|
import { API, getLogo, getSystemName, isAdmin, isMobile, showError, showSuccess } from '../helpers';
|
||||||
import '../index.css';
|
import '../index.css';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -24,11 +24,14 @@ import {Nav, Avatar, Dropdown, Layout} from '@douyinfe/semi-ui';
|
|||||||
|
|
||||||
const SiderBar = () => {
|
const SiderBar = () => {
|
||||||
const [userState, userDispatch] = useContext(UserContext);
|
const [userState, userDispatch] = useContext(UserContext);
|
||||||
|
const defaultIsCollapsed = isMobile() || localStorage.getItem('default_collapse_sidebar') === 'true';
|
||||||
|
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
||||||
const [showSidebar, setShowSidebar] = useState(false);
|
|
||||||
const systemName = getSystemName();
|
const systemName = getSystemName();
|
||||||
const logo = getLogo();
|
const logo = getLogo();
|
||||||
|
const [isCollapsed, setIsCollapsed] = useState(defaultIsCollapsed);
|
||||||
|
|
||||||
const headerButtons = useMemo(() => [
|
const headerButtons = useMemo(() => [
|
||||||
{
|
{
|
||||||
text: '首页',
|
text: '首页',
|
||||||
@ -110,15 +113,41 @@ const SiderBar = () => {
|
|||||||
// }
|
// }
|
||||||
], [localStorage.getItem('enable_data_export'), localStorage.getItem('enable_drawing'), localStorage.getItem('chat_link'), isAdmin()]);
|
], [localStorage.getItem('enable_data_export'), localStorage.getItem('enable_drawing'), localStorage.getItem('chat_link'), isAdmin()]);
|
||||||
|
|
||||||
|
const loadStatus = async () => {
|
||||||
|
const res = await API.get('/api/status');
|
||||||
|
const { success, data } = res.data;
|
||||||
|
if (success) {
|
||||||
|
localStorage.setItem('status', JSON.stringify(data));
|
||||||
|
// statusDispatch({ type: 'set', payload: data });
|
||||||
|
localStorage.setItem('system_name', data.system_name);
|
||||||
|
localStorage.setItem('logo', data.logo);
|
||||||
|
localStorage.setItem('footer_html', data.footer_html);
|
||||||
|
localStorage.setItem('quota_per_unit', data.quota_per_unit);
|
||||||
|
localStorage.setItem('display_in_currency', data.display_in_currency);
|
||||||
|
localStorage.setItem('enable_drawing', data.enable_drawing);
|
||||||
|
localStorage.setItem('enable_data_export', data.enable_data_export);
|
||||||
|
localStorage.setItem('data_export_default_time', data.data_export_default_time);
|
||||||
|
localStorage.setItem('default_collapse_sidebar', data.default_collapse_sidebar);
|
||||||
|
if (data.chat_link) {
|
||||||
|
localStorage.setItem('chat_link', data.chat_link);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('chat_link');
|
||||||
|
}
|
||||||
|
if (data.chat_link2) {
|
||||||
|
localStorage.setItem('chat_link2', data.chat_link2);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('chat_link2');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showError('无法正常连接至服务器!');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async function logout() {
|
useEffect(() => {
|
||||||
setShowSidebar(false);
|
loadStatus().then(() => {
|
||||||
await API.get('/api/user/logout');
|
setIsCollapsed(isMobile() || localStorage.getItem('default_collapse_sidebar') === 'true');
|
||||||
showSuccess('注销成功!');
|
});
|
||||||
userDispatch({type: 'logout'});
|
},[])
|
||||||
localStorage.removeItem('user');
|
|
||||||
navigate('/login');
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -127,7 +156,12 @@ const SiderBar = () => {
|
|||||||
<Nav
|
<Nav
|
||||||
// mode={'horizontal'}
|
// mode={'horizontal'}
|
||||||
// bodyStyle={{ height: 100 }}
|
// bodyStyle={{ height: 100 }}
|
||||||
defaultIsCollapsed={isMobile()}
|
defaultIsCollapsed={isMobile() || localStorage.getItem('default_collapse_sidebar') === 'true'}
|
||||||
|
isCollapsed={isCollapsed}
|
||||||
|
onCollapseChange={collapsed => {
|
||||||
|
console.log(collapsed);
|
||||||
|
setIsCollapsed(collapsed);
|
||||||
|
}}
|
||||||
selectedKeys={selectedKeys}
|
selectedKeys={selectedKeys}
|
||||||
renderWrapper={({itemElement, isSubNav, isInSubNav, props}) => {
|
renderWrapper={({itemElement, isSubNav, isInSubNav, props}) => {
|
||||||
const routerMap = {
|
const routerMap = {
|
||||||
|
@ -20,8 +20,10 @@ const SystemSetting = () => {
|
|||||||
EpayId: '',
|
EpayId: '',
|
||||||
EpayKey: '',
|
EpayKey: '',
|
||||||
Price: 7.3,
|
Price: 7.3,
|
||||||
|
MinTopUp: 1,
|
||||||
TopupGroupRatio: '',
|
TopupGroupRatio: '',
|
||||||
PayAddress: '',
|
PayAddress: '',
|
||||||
|
CustomCallbackAddress: '',
|
||||||
Footer: '',
|
Footer: '',
|
||||||
WeChatAuthEnabled: '',
|
WeChatAuthEnabled: '',
|
||||||
WeChatServerAddress: '',
|
WeChatServerAddress: '',
|
||||||
@ -292,8 +294,8 @@ const SystemSetting = () => {
|
|||||||
<Form.Button onClick={submitServerAddress}>
|
<Form.Button onClick={submitServerAddress}>
|
||||||
更新服务器地址
|
更新服务器地址
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider/>
|
||||||
<Header as='h3'>支付设置(当前仅支持易支付接口,使用上方服务器地址作为回调地址!)</Header>
|
<Header as='h3'>支付设置(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)</Header>
|
||||||
<Form.Group widths='equal'>
|
<Form.Group widths='equal'>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='支付地址,不填写则不启用在线支付'
|
label='支付地址,不填写则不启用在线支付'
|
||||||
@ -316,14 +318,31 @@ const SystemSetting = () => {
|
|||||||
name='EpayKey'
|
name='EpayKey'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
<Form.Input
|
|
||||||
label='充值价格(x元/美金)'
|
|
||||||
placeholder='例如:7,就是7元/美金'
|
|
||||||
value={inputs.Price}
|
|
||||||
name='Price'
|
|
||||||
|
|
||||||
min={0}
|
</Form.Group>
|
||||||
onChange={handleInputChange}
|
<Form.Group widths='equal'>
|
||||||
|
<Form.Input
|
||||||
|
label='回调地址,不填写则使用上方服务器地址作为回调地址'
|
||||||
|
placeholder='例如:https://yourdomain.com'
|
||||||
|
value={inputs.CustomCallbackAddress}
|
||||||
|
name='CustomCallbackAddress'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
label='充值价格(x元/美金)'
|
||||||
|
placeholder='例如:7,就是7元/美金'
|
||||||
|
value={inputs.Price}
|
||||||
|
name='Price'
|
||||||
|
min={0}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
label='最低充值数量'
|
||||||
|
placeholder='例如:2,就是最低充值2$'
|
||||||
|
value={inputs.MinTopUp}
|
||||||
|
name='MinTopUp'
|
||||||
|
min={1}
|
||||||
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Group widths='equal'>
|
<Form.Group widths='equal'>
|
||||||
|
@ -153,7 +153,7 @@ const TokensTable = () => {
|
|||||||
[
|
[
|
||||||
{node: 'item', key: 'next', disabled: !localStorage.getItem('chat_link'), name: 'ChatGPT Next Web', onClick: () => {onOpenLink('next', record.key)}},
|
{node: 'item', key: 'next', disabled: !localStorage.getItem('chat_link'), name: 'ChatGPT Next Web', onClick: () => {onOpenLink('next', record.key)}},
|
||||||
{node: 'item', key: 'next-mj', disabled: !localStorage.getItem('chat_link2'), name: 'ChatGPT Web & Midjourney', onClick: () => {onOpenLink('next-mj', record.key)}},
|
{node: 'item', key: 'next-mj', disabled: !localStorage.getItem('chat_link2'), name: 'ChatGPT Web & Midjourney', onClick: () => {onOpenLink('next-mj', record.key)}},
|
||||||
{node: 'item', key: 'ama', name: 'AMA 问天(BotGrem)', onClick: () => {onOpenLink('ama', record.key)}},
|
{node: 'item', key: 'ama', name: 'AMA 问天(BotGem)', onClick: () => {onOpenLink('ama', record.key)}},
|
||||||
{node: 'item', key: 'opencat', name: 'OpenCat', onClick: () => {onOpenLink('opencat', record.key)}},
|
{node: 'item', key: 'opencat', name: 'OpenCat', onClick: () => {onOpenLink('opencat', record.key)}},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,8 @@ export const modelColorMap = {
|
|||||||
'gpt-4-0314': 'rgb(70,130,180)', // 钢蓝色
|
'gpt-4-0314': 'rgb(70,130,180)', // 钢蓝色
|
||||||
'gpt-4-0613': 'rgb(100,149,237)', // 矢车菊蓝
|
'gpt-4-0613': 'rgb(100,149,237)', // 矢车菊蓝
|
||||||
'gpt-4-1106-preview': 'rgb(30,144,255)', // 道奇蓝
|
'gpt-4-1106-preview': 'rgb(30,144,255)', // 道奇蓝
|
||||||
|
'gpt-4-0125-preview': 'rgb(2,177,236)', // 深天蓝
|
||||||
|
'gpt-4-turbo-preview': 'rgb(2,177,255)', // 深天蓝
|
||||||
'gpt-4-32k': 'rgb(104,111,238)', // 中紫色
|
'gpt-4-32k': 'rgb(104,111,238)', // 中紫色
|
||||||
'gpt-4-32k-0314': 'rgb(90,105,205)', // 暗灰蓝色
|
'gpt-4-32k-0314': 'rgb(90,105,205)', // 暗灰蓝色
|
||||||
'gpt-4-32k-0613': 'rgb(61,71,139)', // 暗蓝灰色
|
'gpt-4-32k-0613': 'rgb(61,71,139)', // 暗蓝灰色
|
||||||
|
@ -10,6 +10,7 @@ const TopUp = () => {
|
|||||||
const [topUpCount, setTopUpCount] = useState(10);
|
const [topUpCount, setTopUpCount] = useState(10);
|
||||||
const [minTopupCount, setMinTopUpCount] = useState(1);
|
const [minTopupCount, setMinTopUpCount] = useState(1);
|
||||||
const [amount, setAmount] = useState(0.0);
|
const [amount, setAmount] = useState(0.0);
|
||||||
|
const [minTopUp, setMinTopUp] = useState(1);
|
||||||
const [topUpLink, setTopUpLink] = useState('');
|
const [topUpLink, setTopUpLink] = useState('');
|
||||||
const [enableOnlineTopUp, setEnableOnlineTopUp] = useState(false);
|
const [enableOnlineTopUp, setEnableOnlineTopUp] = useState(false);
|
||||||
const [userQuota, setUserQuota] = useState(0);
|
const [userQuota, setUserQuota] = useState(0);
|
||||||
@ -61,6 +62,10 @@ const TopUp = () => {
|
|||||||
if (amount === 0) {
|
if (amount === 0) {
|
||||||
await getAmount();
|
await getAmount();
|
||||||
}
|
}
|
||||||
|
if (topUpCount < minTopUp) {
|
||||||
|
showInfo('充值数量不能小于' + minTopUp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setPayWay(payment)
|
setPayWay(payment)
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}
|
}
|
||||||
@ -69,6 +74,10 @@ const TopUp = () => {
|
|||||||
if (amount === 0) {
|
if (amount === 0) {
|
||||||
await getAmount();
|
await getAmount();
|
||||||
}
|
}
|
||||||
|
if (topUpCount < minTopUp) {
|
||||||
|
showInfo('充值数量不能小于' + minTopUp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
try {
|
try {
|
||||||
const res = await API.post('/api/user/pay', {
|
const res = await API.post('/api/user/pay', {
|
||||||
@ -132,6 +141,9 @@ const TopUp = () => {
|
|||||||
if (status.top_up_link) {
|
if (status.top_up_link) {
|
||||||
setTopUpLink(status.top_up_link);
|
setTopUpLink(status.top_up_link);
|
||||||
}
|
}
|
||||||
|
if (status.min_topup) {
|
||||||
|
setMinTopUp(status.min_topup);
|
||||||
|
}
|
||||||
if (status.enable_online_topup) {
|
if (status.enable_online_topup) {
|
||||||
setEnableOnlineTopUp(status.enable_online_topup);
|
setEnableOnlineTopUp(status.enable_online_topup);
|
||||||
}
|
}
|
||||||
@ -239,12 +251,13 @@ const TopUp = () => {
|
|||||||
disabled={!enableOnlineTopUp}
|
disabled={!enableOnlineTopUp}
|
||||||
field={'redemptionCount'}
|
field={'redemptionCount'}
|
||||||
label={'实付金额:' + renderAmount()}
|
label={'实付金额:' + renderAmount()}
|
||||||
placeholder='充值数量'
|
placeholder={'充值数量,最低' + minTopUp + '$'}
|
||||||
name='redemptionCount'
|
name='redemptionCount'
|
||||||
type={'number'}
|
type={'number'}
|
||||||
value={topUpCount}
|
value={topUpCount}
|
||||||
suffix={'$'}
|
suffix={'$'}
|
||||||
min={1}
|
min={minTopUp}
|
||||||
|
defaultValue={minTopUp}
|
||||||
max={100000}
|
max={100000}
|
||||||
onChange={async (value) => {
|
onChange={async (value) => {
|
||||||
if (value < 1) {
|
if (value < 1) {
|
||||||
|
Loading…
Reference in New Issue
Block a user