new-api/controller/token.go

360 lines
9.1 KiB
Go
Raw Normal View History

2023-04-23 11:31:00 +08:00
package controller
import (
"fmt"
2023-04-23 11:31:00 +08:00
"net/http"
"strconv"
2025-04-29 17:13:28 +08:00
"strings"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/i18n"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/setting/operation_setting"
2025-04-29 17:13:28 +08:00
"github.com/gin-gonic/gin"
2023-04-23 11:31:00 +08:00
)
func buildMaskedTokenResponse(token *model.Token) *model.Token {
if token == nil {
return nil
}
maskedToken := *token
maskedToken.Key = token.GetMaskedKey()
return &maskedToken
}
func buildMaskedTokenResponses(tokens []*model.Token) []*model.Token {
maskedTokens := make([]*model.Token, 0, len(tokens))
for _, token := range tokens {
maskedTokens = append(maskedTokens, buildMaskedTokenResponse(token))
}
return maskedTokens
}
2023-04-23 11:31:00 +08:00
func GetAllTokens(c *gin.Context) {
userId := c.GetInt("id")
pageInfo := common.GetPageQuery(c)
tokens, err := model.GetAllUserTokens(userId, pageInfo.GetStartIdx(), pageInfo.GetPageSize())
2023-04-23 11:31:00 +08:00
if err != nil {
common.ApiError(c, err)
2023-04-23 11:31:00 +08:00
return
}
total, _ := model.CountUserTokens(userId)
pageInfo.SetTotal(int(total))
pageInfo.SetItems(buildMaskedTokenResponses(tokens))
common.ApiSuccess(c, pageInfo)
2023-04-23 11:31:00 +08:00
}
func SearchTokens(c *gin.Context) {
userId := c.GetInt("id")
keyword := c.Query("keyword")
2023-11-10 00:10:41 +08:00
token := c.Query("token")
pageInfo := common.GetPageQuery(c)
tokens, total, err := model.SearchUserTokens(userId, keyword, token, pageInfo.GetStartIdx(), pageInfo.GetPageSize())
2023-04-23 11:31:00 +08:00
if err != nil {
common.ApiError(c, err)
2023-04-23 11:31:00 +08:00
return
}
pageInfo.SetTotal(int(total))
pageInfo.SetItems(buildMaskedTokenResponses(tokens))
common.ApiSuccess(c, pageInfo)
2023-04-23 11:31:00 +08:00
}
func GetToken(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
2023-04-23 12:43:10 +08:00
userId := c.GetInt("id")
2023-04-23 11:31:00 +08:00
if err != nil {
common.ApiError(c, err)
2023-04-23 11:31:00 +08:00
return
}
2023-04-23 12:43:10 +08:00
token, err := model.GetTokenByIds(id, userId)
2023-04-23 11:31:00 +08:00
if err != nil {
common.ApiError(c, err)
2023-04-23 11:31:00 +08:00
return
}
common.ApiSuccess(c, buildMaskedTokenResponse(token))
}
func GetTokenKey(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
userId := c.GetInt("id")
if err != nil {
common.ApiError(c, err)
return
}
token, err := model.GetTokenByIds(id, userId)
if err != nil {
common.ApiError(c, err)
return
}
common.ApiSuccess(c, gin.H{
"key": token.GetFullKey(),
2023-04-23 11:31:00 +08:00
})
}
func GetTokenStatus(c *gin.Context) {
tokenId := c.GetInt("token_id")
userId := c.GetInt("id")
token, err := model.GetTokenByIds(tokenId, userId)
if err != nil {
common.ApiError(c, err)
return
}
expiredAt := token.ExpiredTime
if expiredAt == -1 {
expiredAt = 0
}
c.JSON(http.StatusOK, gin.H{
"object": "credit_summary",
"total_granted": token.RemainQuota,
"total_used": 0, // not supported currently
"total_available": token.RemainQuota,
"expires_at": expiredAt * 1000,
})
}
2025-04-29 17:13:28 +08:00
func GetTokenUsage(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "No Authorization header",
})
return
}
parts := strings.Split(authHeader, " ")
if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" {
c.JSON(http.StatusUnauthorized, gin.H{
"success": false,
"message": "Invalid Bearer token",
})
return
}
tokenKey := parts[1]
token, err := model.GetTokenByKey(strings.TrimPrefix(tokenKey, "sk-"), false)
2025-04-29 17:13:28 +08:00
if err != nil {
common.SysError("failed to get token by key: " + err.Error())
common.ApiErrorI18n(c, i18n.MsgTokenGetInfoFailed)
2025-04-29 17:13:28 +08:00
return
}
expiredAt := token.ExpiredTime
if expiredAt == -1 {
expiredAt = 0
}
c.JSON(http.StatusOK, gin.H{
"code": true,
2025-04-29 17:13:28 +08:00
"message": "ok",
"data": gin.H{
"object": "token_usage",
"name": token.Name,
"total_granted": token.RemainQuota + token.UsedQuota,
"total_used": token.UsedQuota,
"total_available": token.RemainQuota,
"unlimited_quota": token.UnlimitedQuota,
"model_limits": token.GetModelLimitsMap(),
"model_limits_enabled": token.ModelLimitsEnabled,
"expires_at": expiredAt,
2025-04-29 17:13:28 +08:00
},
})
}
2023-04-23 11:31:00 +08:00
func AddToken(c *gin.Context) {
token := model.Token{}
err := c.ShouldBindJSON(&token)
if err != nil {
common.ApiError(c, err)
2023-04-23 11:31:00 +08:00
return
}
if len(token.Name) > 50 {
common.ApiErrorI18n(c, i18n.MsgTokenNameTooLong)
2023-04-23 12:43:10 +08:00
return
}
// 非无限额度时,检查额度值是否超出有效范围
if !token.UnlimitedQuota {
if token.RemainQuota < 0 {
common.ApiErrorI18n(c, i18n.MsgTokenQuotaNegative)
return
}
maxQuotaValue := int((1000000000 * common.QuotaPerUnit))
if token.RemainQuota > maxQuotaValue {
common.ApiErrorI18n(c, i18n.MsgTokenQuotaExceedMax, map[string]any{"Max": maxQuotaValue})
return
}
}
// 检查用户令牌数量是否已达上限
maxTokens := operation_setting.GetMaxUserTokens()
count, err := model.CountUserTokens(c.GetInt("id"))
if err != nil {
common.ApiError(c, err)
return
}
if int(count) >= maxTokens {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": fmt.Sprintf("已达到最大令牌数量限制 (%d)", maxTokens),
})
return
}
2024-09-24 20:19:18 +08:00
key, err := common.GenerateKey()
if err != nil {
common.ApiErrorI18n(c, i18n.MsgTokenGenerateFailed)
common.SysLog("failed to generate token key: " + err.Error())
2024-09-24 20:19:18 +08:00
return
}
2023-04-23 12:43:10 +08:00
cleanToken := model.Token{
2024-01-11 19:18:48 +08:00
UserId: c.GetInt("id"),
Name: token.Name,
2024-09-24 20:19:18 +08:00
Key: key,
2024-01-11 19:18:48 +08:00
CreatedTime: common.GetTimestamp(),
AccessedTime: common.GetTimestamp(),
ExpiredTime: token.ExpiredTime,
RemainQuota: token.RemainQuota,
UnlimitedQuota: token.UnlimitedQuota,
ModelLimitsEnabled: token.ModelLimitsEnabled,
ModelLimits: token.ModelLimits,
2024-09-17 20:49:51 +08:00
AllowIps: token.AllowIps,
2024-09-18 05:19:10 +08:00
Group: token.Group,
CrossGroupRetry: token.CrossGroupRetry,
2023-04-23 12:43:10 +08:00
}
err = cleanToken.Insert()
2023-04-23 11:31:00 +08:00
if err != nil {
common.ApiError(c, err)
2023-04-23 11:31:00 +08:00
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
})
}
func DeleteToken(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
2023-04-23 12:43:10 +08:00
userId := c.GetInt("id")
err := model.DeleteTokenById(id, userId)
2023-04-23 11:31:00 +08:00
if err != nil {
common.ApiError(c, err)
2023-04-23 11:31:00 +08:00
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
})
}
func UpdateToken(c *gin.Context) {
2023-04-23 12:43:10 +08:00
userId := c.GetInt("id")
statusOnly := c.Query("status_only")
2023-04-23 11:31:00 +08:00
token := model.Token{}
err := c.ShouldBindJSON(&token)
if err != nil {
common.ApiError(c, err)
2023-04-23 11:31:00 +08:00
return
}
if len(token.Name) > 50 {
common.ApiErrorI18n(c, i18n.MsgTokenNameTooLong)
return
}
if !token.UnlimitedQuota {
if token.RemainQuota < 0 {
common.ApiErrorI18n(c, i18n.MsgTokenQuotaNegative)
return
}
maxQuotaValue := int((1000000000 * common.QuotaPerUnit))
if token.RemainQuota > maxQuotaValue {
common.ApiErrorI18n(c, i18n.MsgTokenQuotaExceedMax, map[string]any{"Max": maxQuotaValue})
return
}
}
2023-04-23 12:43:10 +08:00
cleanToken, err := model.GetTokenByIds(token.Id, userId)
if err != nil {
common.ApiError(c, err)
2023-04-23 12:43:10 +08:00
return
}
if token.Status == common.TokenStatusEnabled {
if cleanToken.Status == common.TokenStatusExpired && cleanToken.ExpiredTime <= common.GetTimestamp() && cleanToken.ExpiredTime != -1 {
common.ApiErrorI18n(c, i18n.MsgTokenExpiredCannotEnable)
return
}
2023-04-28 14:57:20 +08:00
if cleanToken.Status == common.TokenStatusExhausted && cleanToken.RemainQuota <= 0 && !cleanToken.UnlimitedQuota {
common.ApiErrorI18n(c, i18n.MsgTokenExhaustedCannotEable)
return
}
}
if statusOnly != "" {
cleanToken.Status = token.Status
} else {
// If you add more fields, please also update token.Update()
cleanToken.Name = token.Name
cleanToken.ExpiredTime = token.ExpiredTime
2023-05-16 12:09:17 +08:00
cleanToken.RemainQuota = token.RemainQuota
cleanToken.UnlimitedQuota = token.UnlimitedQuota
cleanToken.ModelLimitsEnabled = token.ModelLimitsEnabled
cleanToken.ModelLimits = token.ModelLimits
2024-09-17 20:49:51 +08:00
cleanToken.AllowIps = token.AllowIps
2024-09-18 05:19:10 +08:00
cleanToken.Group = token.Group
cleanToken.CrossGroupRetry = token.CrossGroupRetry
}
2023-04-23 12:43:10 +08:00
err = cleanToken.Update()
2023-04-23 11:31:00 +08:00
if err != nil {
common.ApiError(c, err)
2023-04-23 11:31:00 +08:00
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
"data": buildMaskedTokenResponse(cleanToken),
2023-04-23 11:31:00 +08:00
})
}
type TokenBatch struct {
Ids []int `json:"ids"`
}
func DeleteTokenBatch(c *gin.Context) {
tokenBatch := TokenBatch{}
if err := c.ShouldBindJSON(&tokenBatch); err != nil || len(tokenBatch.Ids) == 0 {
common.ApiErrorI18n(c, i18n.MsgInvalidParams)
2023-04-23 11:31:00 +08:00
return
}
userId := c.GetInt("id")
count, err := model.BatchDeleteTokens(tokenBatch.Ids, userId)
if err != nil {
common.ApiError(c, err)
return
}
2023-04-23 11:31:00 +08:00
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
"data": count,
2023-04-23 11:31:00 +08:00
})
}
func GetTokenKeysBatch(c *gin.Context) {
tokenBatch := TokenBatch{}
if err := c.ShouldBindJSON(&tokenBatch); err != nil || len(tokenBatch.Ids) == 0 {
common.ApiErrorI18n(c, i18n.MsgInvalidParams)
return
}
if len(tokenBatch.Ids) > 100 {
common.ApiErrorI18n(c, i18n.MsgBatchTooMany, map[string]any{"Max": 100})
return
}
userId := c.GetInt("id")
tokens, err := model.GetTokenKeysByIds(tokenBatch.Ids, userId)
if err != nil {
common.ApiError(c, err)
return
}
keysMap := make(map[int]string)
for _, t := range tokens {
keysMap[t.Id] = t.GetFullKey()
}
common.ApiSuccess(c, gin.H{"keys": keysMap})
}