Просмотр исходного кода

Merge branch 'main' into telegram-login

Ehco 2 лет назад
Родитель
Сommit
02d5a5f16d

+ 10 - 4
common/constants.go

@@ -9,14 +9,19 @@ import (
 	"github.com/google/uuid"
 	"github.com/google/uuid"
 )
 )
 
 
-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 SystemName = "New API"
-var ServerAddress = "http://localhost:3000"
+// Pay Settings
+
 var PayAddress = ""
 var PayAddress = ""
+var CustomCallbackAddress = ""
 var EpayId = ""
 var EpayId = ""
 var EpayKey = ""
 var EpayKey = ""
 var Price = 7.3
 var Price = 7.3
+var MinTopUp = 1
+
+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 SystemName = "New API"
+var ServerAddress = "http://localhost:3000"
 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
 
 

+ 4 - 1
common/model-ratio.go

@@ -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

+ 2 - 1
controller/channel.go

@@ -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,

+ 2 - 0
controller/misc.go

@@ -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 != "",
 		},
 		},
 	})
 	})

+ 7 - 0
controller/model.go

@@ -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 {

+ 8 - 8
controller/topup.go

@@ -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 {
-		c.JSON(200, gin.H{"message": "充值金额不能小于1", "data": 10})
+	if req.Amount < common.MinTopUp {
+		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 {
-		c.JSON(200, gin.H{"message": "error", "data": "充值金额不能小于1"})
+	if req.Amount < common.MinTopUp {
+		c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", common.MinTopUp)})
 		return
 		return
 	}
 	}
 	id := c.GetInt("id")
 	id := c.GetInt("id")

+ 10 - 7
model/cache.go

@@ -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 all weights are 0, select a channel randomly
-		return channels[rand.Intn(endIdx)], nil
-	}
+	//if totalWeight == 0 {
+	//	// If all weights are 0, select a channel randomly
+	//	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()
-		if randomWeight <= 0 {
+		randomWeight -= channel.GetWeight() + smoothingFactor
+		if randomWeight < 0 {
 			return channel, nil
 			return channel, nil
 		}
 		}
 	}
 	}

+ 26 - 8
model/channel.go

@@ -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`"
-		if common.UsingPostgreSQL {
-			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
+		whereClause = "(id = ? OR name LIKE ? OR " + keyCol + " = ?) AND " + groupCol + " LIKE ? AND " + modelsCol + " LIKE ?"
+		args = append(args, common.String2Int(keyword), "%"+keyword+"%", keyword, "%"+group+"%", "%"+model+"%")
 	} 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) {

+ 10 - 1
model/option.go

@@ -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":

+ 2 - 2
relay/channel/openai/adaptor.go

@@ -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
 }
 }

+ 2 - 2
relay/channel/openai/relay-openai.go

@@ -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 {

+ 7 - 3
relay/channel/zhipu_v4/adaptor.go → relay/channel/zhipu_4v/adaptor.go

@@ -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
 }
 }

+ 2 - 2
relay/channel/zhipu_v4/constants.go → relay/channel/zhipu_4v/constants.go

@@ -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 - 1
relay/channel/zhipu_v4/dto.go → relay/channel/zhipu_4v/dto.go

@@ -1,4 +1,4 @@
-package zhipu_v4
+package zhipu_4v
 
 
 import (
 import (
 	"one-api/dto"
 	"one-api/dto"

+ 1 - 1
relay/channel/zhipu_v4/relay-zhipu_v4.go → relay/channel/zhipu_4v/relay-zhipu_v4.go

@@ -1,4 +1,4 @@
-package zhipu_v4
+package zhipu_4v
 
 
 import (
 import (
 	"bufio"
 	"bufio"

+ 1 - 0
relay/relay-text.go

@@ -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
 }
 }
 
 

+ 2 - 2
relay/relay_adaptor.go

@@ -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
 }
 }

+ 1 - 1
router/api-router.go

@@ -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 - 0
service/epay.go

@@ -0,0 +1,10 @@
+package service
+
+import "one-api/common"
+
+func GetCallbackAddress() string {
+	if common.CustomCallbackAddress == "" {
+		return common.ServerAddress
+	}
+	return common.CustomCallbackAddress
+}

+ 1 - 39
web/src/App.js

@@ -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;

+ 20 - 8
web/src/components/ChannelsTable.js

@@ -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) => {
-        if (searchKeyword === '' && searchGroup === '') {
+    const searchChannels = async (searchKeyword, searchGroup, searchModel) => {
+        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'
-                            label='关键词'
+                            field='search_keyword'
+                            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>

+ 12 - 1
web/src/components/OperationSetting.js

@@ -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();

+ 46 - 12
web/src/components/SiderBar.js

@@ -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() {
-        setShowSidebar(false);
-        await API.get('/api/user/logout');
-        showSuccess('注销成功!');
-        userDispatch({type: 'logout'});
-        localStorage.removeItem('user');
-        navigate('/login');
-    }
+    useEffect(() => {
+        loadStatus().then(() => {
+            setIsCollapsed(isMobile() || localStorage.getItem('default_collapse_sidebar') === 'true');
+        });
+    },[])
 
 
     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 = {

+ 28 - 9
web/src/components/SystemSetting.js

@@ -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 />
-                    <Header as='h3'>支付设置(当前仅支持易支付接口,使用上方服务器地址作为回调地址!)</Header>
+                    <Divider/>
+                    <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}
-                            onChange={handleInputChange}
+                    </Form.Group>
+                    <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'>

+ 1 - 1
web/src/components/TokensTable.js

@@ -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)}},
                             ]
                             ]
                         }
                         }

+ 2 - 0
web/src/helpers/render.js

@@ -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)',  // 暗蓝灰色

+ 15 - 2
web/src/pages/TopUp/index.js

@@ -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) {