feat(backend): 完善 Gemini OAuth Token 处理
- 修复 account_handler 中 token 字段类型转换(int64 转 string) - 增强 Account.GetCredential 支持多种数值类型(float64, int, json.Number 等) - 添加 Account.IsGemini() 方法用于平台判断 - 优化 refresh_token 和 scope 的空值处理
This commit is contained in:
@@ -2,6 +2,7 @@ package admin
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/model"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/claude"
|
||||
@@ -375,18 +376,22 @@ func (h *AccountHandler) Refresh(c *gin.Context) {
|
||||
newCredentials[k] = v
|
||||
}
|
||||
|
||||
// Update token-related fields
|
||||
newCredentials["access_token"] = tokenInfo.AccessToken
|
||||
newCredentials["token_type"] = tokenInfo.TokenType
|
||||
newCredentials["expires_in"] = tokenInfo.ExpiresIn
|
||||
newCredentials["expires_at"] = tokenInfo.ExpiresAt
|
||||
newCredentials["refresh_token"] = tokenInfo.RefreshToken
|
||||
newCredentials["scope"] = tokenInfo.Scope
|
||||
}
|
||||
// Update token-related fields
|
||||
newCredentials["access_token"] = tokenInfo.AccessToken
|
||||
newCredentials["token_type"] = tokenInfo.TokenType
|
||||
newCredentials["expires_in"] = strconv.FormatInt(tokenInfo.ExpiresIn, 10)
|
||||
newCredentials["expires_at"] = strconv.FormatInt(tokenInfo.ExpiresAt, 10)
|
||||
if strings.TrimSpace(tokenInfo.RefreshToken) != "" {
|
||||
newCredentials["refresh_token"] = tokenInfo.RefreshToken
|
||||
}
|
||||
if strings.TrimSpace(tokenInfo.Scope) != "" {
|
||||
newCredentials["scope"] = tokenInfo.Scope
|
||||
}
|
||||
}
|
||||
|
||||
updatedAccount, err := h.adminService.UpdateAccount(c.Request.Context(), accountID, &service.UpdateAccountInput{
|
||||
Credentials: newCredentials,
|
||||
})
|
||||
updatedAccount, err := h.adminService.UpdateAccount(c.Request.Context(), accountID, &service.UpdateAccountInput{
|
||||
Credentials: newCredentials,
|
||||
})
|
||||
if err != nil {
|
||||
response.ErrorFrom(c, err)
|
||||
return
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
@@ -128,8 +129,37 @@ func (a *Account) GetCredential(key string) string {
|
||||
return ""
|
||||
}
|
||||
if v, ok := a.Credentials[key]; ok {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
return vv
|
||||
case json.Number:
|
||||
return vv.String()
|
||||
case float64:
|
||||
// JSON numbers decode to float64; keep integer formatting for integer-like values.
|
||||
i := int64(vv)
|
||||
if vv == float64(i) {
|
||||
return strconv.FormatInt(i, 10)
|
||||
}
|
||||
return strconv.FormatFloat(vv, 'f', -1, 64)
|
||||
case float32:
|
||||
f := float64(vv)
|
||||
i := int64(f)
|
||||
if f == float64(i) {
|
||||
return strconv.FormatInt(i, 10)
|
||||
}
|
||||
return strconv.FormatFloat(f, 'f', -1, 64)
|
||||
case int:
|
||||
return strconv.FormatInt(int64(vv), 10)
|
||||
case int64:
|
||||
return strconv.FormatInt(vv, 10)
|
||||
case int32:
|
||||
return strconv.FormatInt(int64(vv), 10)
|
||||
case uint:
|
||||
return strconv.FormatUint(uint64(vv), 10)
|
||||
case uint64:
|
||||
return strconv.FormatUint(vv, 10)
|
||||
case uint32:
|
||||
return strconv.FormatUint(uint64(vv), 10)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
@@ -291,6 +321,11 @@ func (a *Account) IsAnthropic() bool {
|
||||
return a.Platform == PlatformAnthropic
|
||||
}
|
||||
|
||||
// IsGemini 检查是否为 Gemini 平台账号
|
||||
func (a *Account) IsGemini() bool {
|
||||
return a.Platform == PlatformGemini
|
||||
}
|
||||
|
||||
// IsOpenAIOAuth 检查是否为 OpenAI OAuth 类型账号
|
||||
func (a *Account) IsOpenAIOAuth() bool {
|
||||
return a.IsOpenAI() && a.Type == AccountTypeOAuth
|
||||
|
||||
@@ -139,7 +139,8 @@ func (s *GeminiOAuthService) ExchangeCode(ctx context.Context, input *GeminiExch
|
||||
}
|
||||
s.sessionStore.Delete(input.SessionID)
|
||||
|
||||
expiresAt := time.Now().Unix() + tokenResp.ExpiresIn
|
||||
// 计算过期时间时减去 5 分钟安全时间窗口,考虑网络延迟和时钟偏差
|
||||
expiresAt := time.Now().Unix() + tokenResp.ExpiresIn - 300
|
||||
projectID, _ := s.fetchProjectID(ctx, tokenResp.AccessToken, proxyURL)
|
||||
|
||||
return &GeminiTokenInfo{
|
||||
@@ -167,7 +168,8 @@ func (s *GeminiOAuthService) RefreshToken(ctx context.Context, refreshToken, pro
|
||||
|
||||
tokenResp, err := s.oauthClient.RefreshToken(ctx, refreshToken, proxyURL)
|
||||
if err == nil {
|
||||
expiresAt := time.Now().Unix() + tokenResp.ExpiresIn
|
||||
// 计算过期时间时减去 5 分钟安全时间窗口,考虑网络延迟和时钟偏差
|
||||
expiresAt := time.Now().Unix() + tokenResp.ExpiresIn - 300
|
||||
return &GeminiTokenInfo{
|
||||
AccessToken: tokenResp.AccessToken,
|
||||
RefreshToken: tokenResp.RefreshToken,
|
||||
|
||||
Reference in New Issue
Block a user