mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-12-25 09:15:56 +08:00
Compare commits
4 Commits
v0.4.5-alp
...
v0.4.7-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a4cd403fd | ||
|
|
9ac5410d06 | ||
|
|
7edc2b5376 | ||
|
|
d4869dfad2 |
@@ -18,7 +18,8 @@ var Logo = ""
|
|||||||
var TopUpLink = ""
|
var TopUpLink = ""
|
||||||
var ChatLink = ""
|
var ChatLink = ""
|
||||||
var QuotaPerUnit = 500 * 1000.0 // $0.002 / 1K tokens
|
var QuotaPerUnit = 500 * 1000.0 // $0.002 / 1K tokens
|
||||||
var DisplayInCurrencyEnabled = false
|
var DisplayInCurrencyEnabled = true
|
||||||
|
var DisplayTokenStatEnabled = true
|
||||||
|
|
||||||
var UsingSQLite = false
|
var UsingSQLite = false
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetSubscription(c *gin.Context) {
|
func GetSubscription(c *gin.Context) {
|
||||||
userId := c.GetInt("id")
|
var quota int
|
||||||
quota, err := model.GetUserQuota(userId)
|
var err error
|
||||||
|
var token *model.Token
|
||||||
|
if common.DisplayTokenStatEnabled {
|
||||||
|
tokenId := c.GetInt("token_id")
|
||||||
|
token, err = model.GetTokenById(tokenId)
|
||||||
|
quota = token.RemainQuota
|
||||||
|
} else {
|
||||||
|
userId := c.GetInt("id")
|
||||||
|
quota, err = model.GetUserQuota(userId)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
openAIError := OpenAIError{
|
openAIError := OpenAIError{
|
||||||
Message: err.Error(),
|
Message: err.Error(),
|
||||||
@@ -35,8 +44,17 @@ func GetSubscription(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetUsage(c *gin.Context) {
|
func GetUsage(c *gin.Context) {
|
||||||
userId := c.GetInt("id")
|
var quota int
|
||||||
quota, err := model.GetUserUsedQuota(userId)
|
var err error
|
||||||
|
var token *model.Token
|
||||||
|
if common.DisplayTokenStatEnabled {
|
||||||
|
tokenId := c.GetInt("token_id")
|
||||||
|
token, err = model.GetTokenById(tokenId)
|
||||||
|
quota = token.UsedQuota
|
||||||
|
} else {
|
||||||
|
userId := c.GetInt("id")
|
||||||
|
quota, err = model.GetUserUsedQuota(userId)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
openAIError := OpenAIError{
|
openAIError := OpenAIError{
|
||||||
Message: err.Error(),
|
Message: err.Error(),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func GetOptions(c *gin.Context) {
|
|||||||
var options []*model.Option
|
var options []*model.Option
|
||||||
common.OptionMapRWMutex.Lock()
|
common.OptionMapRWMutex.Lock()
|
||||||
for k, v := range common.OptionMap {
|
for k, v := range common.OptionMap {
|
||||||
if strings.Contains(k, "Token") || strings.Contains(k, "Secret") {
|
if strings.HasSuffix(k, "Token") || strings.HasSuffix(k, "Secret") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
options = append(options, &model.Option{
|
options = append(options, &model.Option{
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
preConsumedQuota := int(float64(preConsumedTokens) * ratio)
|
preConsumedQuota := int(float64(preConsumedTokens) * ratio)
|
||||||
userQuota, err := model.CacheGetUserQuota(userId)
|
userQuota, err := model.CacheGetUserQuota(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "get_user_quota_failed", http.StatusOK)
|
return errorWrapper(err, "get_user_quota_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
if userQuota > 10*preConsumedQuota {
|
if userQuota > 10*preConsumedQuota {
|
||||||
// in this case, we do not pre-consume quota
|
// in this case, we do not pre-consume quota
|
||||||
@@ -86,12 +86,12 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
if consumeQuota && preConsumedQuota > 0 {
|
if consumeQuota && preConsumedQuota > 0 {
|
||||||
err := model.PreConsumeTokenQuota(tokenId, preConsumedQuota)
|
err := model.PreConsumeTokenQuota(tokenId, preConsumedQuota)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "pre_consume_token_quota_failed", http.StatusOK)
|
return errorWrapper(err, "pre_consume_token_quota_failed", http.StatusForbidden)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest(c.Request.Method, fullRequestURL, c.Request.Body)
|
req, err := http.NewRequest(c.Request.Method, fullRequestURL, c.Request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "new_request_failed", http.StatusOK)
|
return errorWrapper(err, "new_request_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
if channelType == common.ChannelTypeAzure {
|
if channelType == common.ChannelTypeAzure {
|
||||||
key := c.Request.Header.Get("Authorization")
|
key := c.Request.Header.Get("Authorization")
|
||||||
@@ -106,15 +106,15 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "do_request_failed", http.StatusOK)
|
return errorWrapper(err, "do_request_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
err = req.Body.Close()
|
err = req.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "close_request_body_failed", http.StatusOK)
|
return errorWrapper(err, "close_request_body_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
err = c.Request.Body.Close()
|
err = c.Request.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "close_request_body_failed", http.StatusOK)
|
return errorWrapper(err, "close_request_body_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
var textResponse TextResponse
|
var textResponse TextResponse
|
||||||
isStream := strings.HasPrefix(resp.Header.Get("Content-Type"), "text/event-stream")
|
isStream := strings.HasPrefix(resp.Header.Get("Content-Type"), "text/event-stream")
|
||||||
@@ -224,22 +224,22 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
})
|
})
|
||||||
err = resp.Body.Close()
|
err = resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "close_response_body_failed", http.StatusOK)
|
return errorWrapper(err, "close_response_body_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
if consumeQuota {
|
if consumeQuota {
|
||||||
responseBody, err := io.ReadAll(resp.Body)
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "read_response_body_failed", http.StatusOK)
|
return errorWrapper(err, "read_response_body_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
err = resp.Body.Close()
|
err = resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "close_response_body_failed", http.StatusOK)
|
return errorWrapper(err, "close_response_body_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(responseBody, &textResponse)
|
err = json.Unmarshal(responseBody, &textResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "unmarshal_response_body_failed", http.StatusOK)
|
return errorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
if textResponse.Error.Type != "" {
|
if textResponse.Error.Type != "" {
|
||||||
return &OpenAIErrorWithStatusCode{
|
return &OpenAIErrorWithStatusCode{
|
||||||
@@ -260,11 +260,11 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
c.Writer.WriteHeader(resp.StatusCode)
|
c.Writer.WriteHeader(resp.StatusCode)
|
||||||
_, err = io.Copy(c.Writer, resp.Body)
|
_, err = io.Copy(c.Writer, resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "copy_response_body_failed", http.StatusOK)
|
return errorWrapper(err, "copy_response_body_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
err = resp.Body.Close()
|
err = resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errorWrapper(err, "close_response_body_failed", http.StatusOK)
|
return errorWrapper(err, "close_response_body_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ func RelayNotImplemented(c *gin.Context) {
|
|||||||
Param: "",
|
Param: "",
|
||||||
Code: "api_not_implemented",
|
Code: "api_not_implemented",
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusNotImplemented, gin.H{
|
||||||
"error": err,
|
"error": err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -147,7 +147,7 @@ func RelayNotFound(c *gin.Context) {
|
|||||||
Param: "",
|
Param: "",
|
||||||
Code: "api_not_found",
|
Code: "api_not_found",
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusNotFound, gin.H{
|
||||||
"error": err,
|
"error": err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func TokenAuth() func(c *gin.Context) {
|
|||||||
key = parts[0]
|
key = parts[0]
|
||||||
token, err := model.ValidateUserToken(key)
|
token, err := model.ValidateUserToken(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusUnauthorized, gin.H{
|
||||||
"error": gin.H{
|
"error": gin.H{
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
"type": "one_api_error",
|
"type": "one_api_error",
|
||||||
@@ -101,7 +101,7 @@ func TokenAuth() func(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !model.CacheIsUserEnabled(token.UserId) {
|
if !model.CacheIsUserEnabled(token.UserId) {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusForbidden, gin.H{
|
||||||
"error": gin.H{
|
"error": gin.H{
|
||||||
"message": "用户已被封禁",
|
"message": "用户已被封禁",
|
||||||
"type": "one_api_error",
|
"type": "one_api_error",
|
||||||
@@ -123,7 +123,7 @@ func TokenAuth() func(c *gin.Context) {
|
|||||||
if model.IsAdmin(token.UserId) {
|
if model.IsAdmin(token.UserId) {
|
||||||
c.Set("channelId", parts[1])
|
c.Set("channelId", parts[1])
|
||||||
} else {
|
} else {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusForbidden, gin.H{
|
||||||
"error": gin.H{
|
"error": gin.H{
|
||||||
"message": "普通用户不支持指定渠道",
|
"message": "普通用户不支持指定渠道",
|
||||||
"type": "one_api_error",
|
"type": "one_api_error",
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ 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 {
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
"error": gin.H{
|
"error": gin.H{
|
||||||
"message": "无效的渠道 ID",
|
"message": "无效的渠道 ID",
|
||||||
"type": "one_api_error",
|
"type": "one_api_error",
|
||||||
@@ -35,7 +35,7 @@ func Distribute() func(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
channel, err = model.GetChannelById(id, true)
|
channel, err = model.GetChannelById(id, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
"error": gin.H{
|
"error": gin.H{
|
||||||
"message": "无效的渠道 ID",
|
"message": "无效的渠道 ID",
|
||||||
"type": "one_api_error",
|
"type": "one_api_error",
|
||||||
@@ -45,7 +45,7 @@ func Distribute() func(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if channel.Status != common.ChannelStatusEnabled {
|
if channel.Status != common.ChannelStatusEnabled {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(http.StatusForbidden, gin.H{
|
||||||
"error": gin.H{
|
"error": gin.H{
|
||||||
"message": "该渠道已被禁用",
|
"message": "该渠道已被禁用",
|
||||||
"type": "one_api_error",
|
"type": "one_api_error",
|
||||||
@@ -59,7 +59,7 @@ func Distribute() func(c *gin.Context) {
|
|||||||
var modelRequest ModelRequest
|
var modelRequest ModelRequest
|
||||||
err := common.UnmarshalBodyReusable(c, &modelRequest)
|
err := common.UnmarshalBodyReusable(c, &modelRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
"error": gin.H{
|
"error": gin.H{
|
||||||
"message": "无效的请求",
|
"message": "无效的请求",
|
||||||
"type": "one_api_error",
|
"type": "one_api_error",
|
||||||
@@ -75,7 +75,7 @@ func Distribute() func(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
channel, err = model.CacheGetRandomSatisfiedChannel(userGroup, modelRequest.Model)
|
channel, err = model.CacheGetRandomSatisfiedChannel(userGroup, modelRequest.Model)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(http.StatusServiceUnavailable, gin.H{
|
||||||
"error": gin.H{
|
"error": gin.H{
|
||||||
"message": "无可用渠道",
|
"message": "无可用渠道",
|
||||||
"type": "one_api_error",
|
"type": "one_api_error",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ func InitOptionMap() {
|
|||||||
common.OptionMap["AutomaticDisableChannelEnabled"] = strconv.FormatBool(common.AutomaticDisableChannelEnabled)
|
common.OptionMap["AutomaticDisableChannelEnabled"] = strconv.FormatBool(common.AutomaticDisableChannelEnabled)
|
||||||
common.OptionMap["LogConsumeEnabled"] = strconv.FormatBool(common.LogConsumeEnabled)
|
common.OptionMap["LogConsumeEnabled"] = strconv.FormatBool(common.LogConsumeEnabled)
|
||||||
common.OptionMap["DisplayInCurrencyEnabled"] = strconv.FormatBool(common.DisplayInCurrencyEnabled)
|
common.OptionMap["DisplayInCurrencyEnabled"] = strconv.FormatBool(common.DisplayInCurrencyEnabled)
|
||||||
|
common.OptionMap["DisplayTokenStatEnabled"] = strconv.FormatBool(common.DisplayTokenStatEnabled)
|
||||||
common.OptionMap["ChannelDisableThreshold"] = strconv.FormatFloat(common.ChannelDisableThreshold, 'f', -1, 64)
|
common.OptionMap["ChannelDisableThreshold"] = strconv.FormatFloat(common.ChannelDisableThreshold, 'f', -1, 64)
|
||||||
common.OptionMap["SMTPServer"] = ""
|
common.OptionMap["SMTPServer"] = ""
|
||||||
common.OptionMap["SMTPFrom"] = ""
|
common.OptionMap["SMTPFrom"] = ""
|
||||||
@@ -144,6 +145,8 @@ func updateOptionMap(key string, value string) (err error) {
|
|||||||
common.LogConsumeEnabled = boolValue
|
common.LogConsumeEnabled = boolValue
|
||||||
case "DisplayInCurrencyEnabled":
|
case "DisplayInCurrencyEnabled":
|
||||||
common.DisplayInCurrencyEnabled = boolValue
|
common.DisplayInCurrencyEnabled = boolValue
|
||||||
|
case "DisplayTokenStatEnabled":
|
||||||
|
common.DisplayTokenStatEnabled = boolValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch key {
|
switch key {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Token struct {
|
|||||||
ExpiredTime int64 `json:"expired_time" gorm:"bigint;default:-1"` // -1 means never expired
|
ExpiredTime int64 `json:"expired_time" gorm:"bigint;default:-1"` // -1 means never expired
|
||||||
RemainQuota int `json:"remain_quota" gorm:"default:0"`
|
RemainQuota int `json:"remain_quota" gorm:"default:0"`
|
||||||
UnlimitedQuota bool `json:"unlimited_quota" gorm:"default:false"`
|
UnlimitedQuota bool `json:"unlimited_quota" gorm:"default:false"`
|
||||||
|
UsedQuota int `json:"used_quota" gorm:"default:0"` // used quota
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllUserTokens(userId int, startIdx int, num int) ([]*Token, error) {
|
func GetAllUserTokens(userId int, startIdx int, num int) ([]*Token, error) {
|
||||||
@@ -130,7 +131,12 @@ func IncreaseTokenQuota(id int, quota int) (err error) {
|
|||||||
if quota < 0 {
|
if quota < 0 {
|
||||||
return errors.New("quota 不能为负数!")
|
return errors.New("quota 不能为负数!")
|
||||||
}
|
}
|
||||||
err = DB.Model(&Token{}).Where("id = ?", id).Update("remain_quota", gorm.Expr("remain_quota + ?", quota)).Error
|
err = DB.Model(&Token{}).Where("id = ?", id).Updates(
|
||||||
|
map[string]interface{}{
|
||||||
|
"remain_quota": gorm.Expr("remain_quota + ?", quota),
|
||||||
|
"used_quota": gorm.Expr("used_quota - ?", quota),
|
||||||
|
},
|
||||||
|
).Error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +144,12 @@ func DecreaseTokenQuota(id int, quota int) (err error) {
|
|||||||
if quota < 0 {
|
if quota < 0 {
|
||||||
return errors.New("quota 不能为负数!")
|
return errors.New("quota 不能为负数!")
|
||||||
}
|
}
|
||||||
err = DB.Model(&Token{}).Where("id = ?", id).Update("remain_quota", gorm.Expr("remain_quota - ?", quota)).Error
|
err = DB.Model(&Token{}).Where("id = ?", id).Updates(
|
||||||
|
map[string]interface{}{
|
||||||
|
"remain_quota": gorm.Expr("remain_quota - ?", quota),
|
||||||
|
"used_quota": gorm.Expr("used_quota + ?", quota),
|
||||||
|
},
|
||||||
|
).Error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ const OperationSetting = () => {
|
|||||||
AutomaticDisableChannelEnabled: '',
|
AutomaticDisableChannelEnabled: '',
|
||||||
ChannelDisableThreshold: 0,
|
ChannelDisableThreshold: 0,
|
||||||
LogConsumeEnabled: '',
|
LogConsumeEnabled: '',
|
||||||
DisplayInCurrencyEnabled: ''
|
DisplayInCurrencyEnabled: '',
|
||||||
|
DisplayTokenStatEnabled: ''
|
||||||
});
|
});
|
||||||
const [originInputs, setOriginInputs] = useState({});
|
const [originInputs, setOriginInputs] = useState({});
|
||||||
let [loading, setLoading] = useState(false);
|
let [loading, setLoading] = useState(false);
|
||||||
@@ -177,6 +178,12 @@ const OperationSetting = () => {
|
|||||||
name='DisplayInCurrencyEnabled'
|
name='DisplayInCurrencyEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
<Form.Checkbox
|
||||||
|
checked={inputs.DisplayTokenStatEnabled === 'true'}
|
||||||
|
label='Billing 相关 API 显示令牌额度而非用户额度'
|
||||||
|
name='DisplayTokenStatEnabled'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={() => {
|
<Form.Button onClick={() => {
|
||||||
submitConfig('general').then();
|
submitConfig('general').then();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Button, Form, Grid, Header, Image, Segment } from 'semantic-ui-react';
|
import { Button, Form, Grid, Header, Image, Segment } from 'semantic-ui-react';
|
||||||
import { API, copy, showError, showSuccess } from '../helpers';
|
import { API, copy, showError, showInfo, showNotice, showSuccess } from '../helpers';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
|
||||||
const PasswordResetConfirm = () => {
|
const PasswordResetConfirm = () => {
|
||||||
@@ -33,7 +33,7 @@ const PasswordResetConfirm = () => {
|
|||||||
if (success) {
|
if (success) {
|
||||||
let password = res.data.data;
|
let password = res.data.data;
|
||||||
await copy(password);
|
await copy(password);
|
||||||
showSuccess(`密码已重置并已复制到剪贴板:${password}`);
|
showNotice(`密码已重置并已复制到剪贴板:${password}`);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,13 +181,21 @@ const TokensTable = () => {
|
|||||||
>
|
>
|
||||||
状态
|
状态
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
|
<Table.HeaderCell
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
sortToken('used_quota');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
已用额度
|
||||||
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
sortToken('remain_quota');
|
sortToken('remain_quota');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
额度
|
剩余额度
|
||||||
</Table.HeaderCell>
|
</Table.HeaderCell>
|
||||||
<Table.HeaderCell
|
<Table.HeaderCell
|
||||||
style={{ cursor: 'pointer' }}
|
style={{ cursor: 'pointer' }}
|
||||||
@@ -221,6 +229,7 @@ const TokensTable = () => {
|
|||||||
<Table.Row key={token.id}>
|
<Table.Row key={token.id}>
|
||||||
<Table.Cell>{token.name ? token.name : '无'}</Table.Cell>
|
<Table.Cell>{token.name ? token.name : '无'}</Table.Cell>
|
||||||
<Table.Cell>{renderStatus(token.status)}</Table.Cell>
|
<Table.Cell>{renderStatus(token.status)}</Table.Cell>
|
||||||
|
<Table.Cell>{renderQuota(token.used_quota)}</Table.Cell>
|
||||||
<Table.Cell>{token.unlimited_quota ? '无限制' : renderQuota(token.remain_quota, 2)}</Table.Cell>
|
<Table.Cell>{token.unlimited_quota ? '无限制' : renderQuota(token.remain_quota, 2)}</Table.Cell>
|
||||||
<Table.Cell>{renderTimestamp(token.created_time)}</Table.Cell>
|
<Table.Cell>{renderTimestamp(token.created_time)}</Table.Cell>
|
||||||
<Table.Cell>{token.expired_time === -1 ? '永不过期' : renderTimestamp(token.expired_time)}</Table.Cell>
|
<Table.Cell>{token.expired_time === -1 ? '永不过期' : renderTimestamp(token.expired_time)}</Table.Cell>
|
||||||
|
|||||||
Reference in New Issue
Block a user