fix: Antigravity 刷新 token 时检测 project_id 缺失
- 刷新 token 后调用 LoadCodeAssist 获取 project_id - 如果获取失败,保留原有 project_id,标记账户为 error - token 仍会正常更新,不影响凭证刷新 - 错误信息:账户缺少project id,可能无法使用Antigravity
This commit is contained in:
@@ -450,6 +450,28 @@ func (h *AccountHandler) Refresh(c *gin.Context) {
|
|||||||
newCredentials[k] = v
|
newCredentials[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果 project_id 获取失败,先更新凭证,再标记账户为 error
|
||||||
|
if tokenInfo.ProjectIDMissing {
|
||||||
|
// 先更新凭证
|
||||||
|
_, updateErr := h.adminService.UpdateAccount(c.Request.Context(), accountID, &service.UpdateAccountInput{
|
||||||
|
Credentials: newCredentials,
|
||||||
|
})
|
||||||
|
if updateErr != nil {
|
||||||
|
response.InternalError(c, "Failed to update credentials: "+updateErr.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 标记账户为 error
|
||||||
|
if setErr := h.adminService.SetAccountError(c.Request.Context(), accountID, "账户缺少project id,可能无法使用Antigravity"); setErr != nil {
|
||||||
|
response.InternalError(c, "Failed to set account error: "+setErr.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.Success(c, gin.H{
|
||||||
|
"message": "Token refreshed but project_id is missing, account marked as error",
|
||||||
|
"warning": "missing_project_id",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Use Anthropic/Claude OAuth service to refresh token
|
// Use Anthropic/Claude OAuth service to refresh token
|
||||||
tokenInfo, err := h.oauthService.RefreshAccountToken(c.Request.Context(), account)
|
tokenInfo, err := h.oauthService.RefreshAccountToken(c.Request.Context(), account)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ type AdminService interface {
|
|||||||
DeleteAccount(ctx context.Context, id int64) error
|
DeleteAccount(ctx context.Context, id int64) error
|
||||||
RefreshAccountCredentials(ctx context.Context, id int64) (*Account, error)
|
RefreshAccountCredentials(ctx context.Context, id int64) (*Account, error)
|
||||||
ClearAccountError(ctx context.Context, id int64) (*Account, error)
|
ClearAccountError(ctx context.Context, id int64) (*Account, error)
|
||||||
|
SetAccountError(ctx context.Context, id int64, errorMsg string) error
|
||||||
SetAccountSchedulable(ctx context.Context, id int64, schedulable bool) (*Account, error)
|
SetAccountSchedulable(ctx context.Context, id int64, schedulable bool) (*Account, error)
|
||||||
BulkUpdateAccounts(ctx context.Context, input *BulkUpdateAccountsInput) (*BulkUpdateAccountsResult, error)
|
BulkUpdateAccounts(ctx context.Context, input *BulkUpdateAccountsInput) (*BulkUpdateAccountsResult, error)
|
||||||
|
|
||||||
@@ -991,6 +992,10 @@ func (s *adminServiceImpl) ClearAccountError(ctx context.Context, id int64) (*Ac
|
|||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *adminServiceImpl) SetAccountError(ctx context.Context, id int64, errorMsg string) error {
|
||||||
|
return s.accountRepo.SetError(ctx, id, errorMsg)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *adminServiceImpl) SetAccountSchedulable(ctx context.Context, id int64, schedulable bool) (*Account, error) {
|
func (s *adminServiceImpl) SetAccountSchedulable(ctx context.Context, id int64, schedulable bool) (*Account, error) {
|
||||||
if err := s.accountRepo.SetSchedulable(ctx, id, schedulable); err != nil {
|
if err := s.accountRepo.SetSchedulable(ctx, id, schedulable); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -82,13 +82,14 @@ type AntigravityExchangeCodeInput struct {
|
|||||||
|
|
||||||
// AntigravityTokenInfo token 信息
|
// AntigravityTokenInfo token 信息
|
||||||
type AntigravityTokenInfo struct {
|
type AntigravityTokenInfo struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
RefreshToken string `json:"refresh_token"`
|
RefreshToken string `json:"refresh_token"`
|
||||||
ExpiresIn int64 `json:"expires_in"`
|
ExpiresIn int64 `json:"expires_in"`
|
||||||
ExpiresAt int64 `json:"expires_at"`
|
ExpiresAt int64 `json:"expires_at"`
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
Email string `json:"email,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
ProjectID string `json:"project_id,omitempty"`
|
ProjectID string `json:"project_id,omitempty"`
|
||||||
|
ProjectIDMissing bool `json:"-"` // LoadCodeAssist 未返回 project_id
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExchangeCode 用 authorization code 交换 token
|
// ExchangeCode 用 authorization code 交换 token
|
||||||
@@ -236,16 +237,15 @@ func (s *AntigravityOAuthService) RefreshAccountToken(ctx context.Context, accou
|
|||||||
tokenInfo.Email = existingEmail
|
tokenInfo.Email = existingEmail
|
||||||
}
|
}
|
||||||
|
|
||||||
// 每次刷新都调用 LoadCodeAssist 更新 project_id
|
// 每次刷新都调用 LoadCodeAssist 获取 project_id
|
||||||
client := antigravity.NewClient(proxyURL)
|
client := antigravity.NewClient(proxyURL)
|
||||||
loadResp, _, err := client.LoadCodeAssist(ctx, tokenInfo.AccessToken)
|
loadResp, _, err := client.LoadCodeAssist(ctx, tokenInfo.AccessToken)
|
||||||
if err != nil {
|
if err != nil || loadResp == nil || loadResp.CloudAICompanionProject == "" {
|
||||||
// 失败时保留原有的 project_id
|
// LoadCodeAssist 失败或返回空,保留原有 project_id,标记缺失
|
||||||
existingProjectID := strings.TrimSpace(account.GetCredential("project_id"))
|
existingProjectID := strings.TrimSpace(account.GetCredential("project_id"))
|
||||||
if existingProjectID != "" {
|
tokenInfo.ProjectID = existingProjectID
|
||||||
tokenInfo.ProjectID = existingProjectID
|
tokenInfo.ProjectIDMissing = true
|
||||||
}
|
} else {
|
||||||
} else if loadResp != nil && loadResp.CloudAICompanionProject != "" {
|
|
||||||
tokenInfo.ProjectID = loadResp.CloudAICompanionProject
|
tokenInfo.ProjectID = loadResp.CloudAICompanionProject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,5 +61,10 @@ func (r *AntigravityTokenRefresher) Refresh(ctx context.Context, account *Accoun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果 project_id 获取失败,返回 credentials 但同时返回错误让账户被标记
|
||||||
|
if tokenInfo.ProjectIDMissing {
|
||||||
|
return newCredentials, fmt.Errorf("missing_project_id: 账户缺少project id,可能无法使用Antigravity")
|
||||||
|
}
|
||||||
|
|
||||||
return newCredentials, nil
|
return newCredentials, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,12 +163,16 @@ func (s *TokenRefreshService) refreshWithRetry(ctx context.Context, account *Acc
|
|||||||
|
|
||||||
for attempt := 1; attempt <= s.cfg.MaxRetries; attempt++ {
|
for attempt := 1; attempt <= s.cfg.MaxRetries; attempt++ {
|
||||||
newCredentials, err := refresher.Refresh(ctx, account)
|
newCredentials, err := refresher.Refresh(ctx, account)
|
||||||
if err == nil {
|
|
||||||
// 刷新成功,更新账号credentials
|
// 如果有新凭证,先更新(即使有错误也要保存 token)
|
||||||
|
if newCredentials != nil {
|
||||||
account.Credentials = newCredentials
|
account.Credentials = newCredentials
|
||||||
if err := s.accountRepo.Update(ctx, account); err != nil {
|
if saveErr := s.accountRepo.Update(ctx, account); saveErr != nil {
|
||||||
return fmt.Errorf("failed to save credentials: %w", err)
|
return fmt.Errorf("failed to save credentials: %w", saveErr)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,6 +223,7 @@ func isNonRetryableRefreshError(err error) bool {
|
|||||||
"invalid_client", // 客户端配置错误
|
"invalid_client", // 客户端配置错误
|
||||||
"unauthorized_client", // 客户端未授权
|
"unauthorized_client", // 客户端未授权
|
||||||
"access_denied", // 访问被拒绝
|
"access_denied", // 访问被拒绝
|
||||||
|
"missing_project_id", // 缺少 project_id
|
||||||
}
|
}
|
||||||
for _, needle := range nonRetryable {
|
for _, needle := range nonRetryable {
|
||||||
if strings.Contains(msg, needle) {
|
if strings.Contains(msg, needle) {
|
||||||
|
|||||||
Reference in New Issue
Block a user