Merge pull request #2374 from gaoren002/fix/openai-refresh-token-reused
fix: mark reused refresh tokens non-retryable and unschedule errored accounts
This commit is contained in:
commit
131d4b3050
@ -317,6 +317,10 @@ func (r *accountRepository) Update(ctx context.Context, account *service.Account
|
|||||||
if account == nil {
|
if account == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
schedulable := account.Schedulable
|
||||||
|
if account.Status == service.StatusError {
|
||||||
|
schedulable = false
|
||||||
|
}
|
||||||
|
|
||||||
builder := r.client.Account.UpdateOneID(account.ID).
|
builder := r.client.Account.UpdateOneID(account.ID).
|
||||||
SetName(account.Name).
|
SetName(account.Name).
|
||||||
@ -329,7 +333,7 @@ func (r *accountRepository) Update(ctx context.Context, account *service.Account
|
|||||||
SetPriority(account.Priority).
|
SetPriority(account.Priority).
|
||||||
SetStatus(account.Status).
|
SetStatus(account.Status).
|
||||||
SetErrorMessage(account.ErrorMessage).
|
SetErrorMessage(account.ErrorMessage).
|
||||||
SetSchedulable(account.Schedulable).
|
SetSchedulable(schedulable).
|
||||||
SetAutoPauseOnExpired(account.AutoPauseOnExpired)
|
SetAutoPauseOnExpired(account.AutoPauseOnExpired)
|
||||||
|
|
||||||
if account.RateMultiplier != nil {
|
if account.RateMultiplier != nil {
|
||||||
@ -716,6 +720,7 @@ func (r *accountRepository) SetError(ctx context.Context, id int64, errorMsg str
|
|||||||
Where(dbaccount.IDEQ(id)).
|
Where(dbaccount.IDEQ(id)).
|
||||||
SetStatus(service.StatusError).
|
SetStatus(service.StatusError).
|
||||||
SetErrorMessage(errorMsg).
|
SetErrorMessage(errorMsg).
|
||||||
|
SetSchedulable(false).
|
||||||
Save(ctx)
|
Save(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@ -729,7 +729,7 @@ func (s *AccountRepoSuite) TestUpdateLastUsed() {
|
|||||||
// --- SetError ---
|
// --- SetError ---
|
||||||
|
|
||||||
func (s *AccountRepoSuite) TestSetError() {
|
func (s *AccountRepoSuite) TestSetError() {
|
||||||
account := mustCreateAccount(s.T(), s.client, &service.Account{Name: "acc-err", Status: service.StatusActive})
|
account := mustCreateAccount(s.T(), s.client, &service.Account{Name: "acc-err", Status: service.StatusActive, Schedulable: true})
|
||||||
|
|
||||||
s.Require().NoError(s.repo.SetError(s.ctx, account.ID, "something went wrong"))
|
s.Require().NoError(s.repo.SetError(s.ctx, account.ID, "something went wrong"))
|
||||||
|
|
||||||
@ -737,6 +737,22 @@ func (s *AccountRepoSuite) TestSetError() {
|
|||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Equal(service.StatusError, got.Status)
|
s.Require().Equal(service.StatusError, got.Status)
|
||||||
s.Require().Equal("something went wrong", got.ErrorMessage)
|
s.Require().Equal("something went wrong", got.ErrorMessage)
|
||||||
|
s.Require().False(got.Schedulable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AccountRepoSuite) TestUpdateErrorStatusUnschedulesAccount() {
|
||||||
|
account := mustCreateAccount(s.T(), s.client, &service.Account{Name: "acc-update-err", Status: service.StatusActive, Schedulable: true})
|
||||||
|
account.Status = service.StatusError
|
||||||
|
account.ErrorMessage = "token revoked"
|
||||||
|
account.Schedulable = true
|
||||||
|
|
||||||
|
s.Require().NoError(s.repo.Update(s.ctx, account))
|
||||||
|
|
||||||
|
got, err := s.repo.GetByID(s.ctx, account.ID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Equal(service.StatusError, got.Status)
|
||||||
|
s.Require().Equal("token revoked", got.ErrorMessage)
|
||||||
|
s.Require().False(got.Schedulable)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AccountRepoSuite) TestClearError_SyncSchedulerSnapshotOnRecovery() {
|
func (s *AccountRepoSuite) TestClearError_SyncSchedulerSnapshotOnRecovery() {
|
||||||
|
|||||||
@ -417,11 +417,12 @@ func isNonRetryableRefreshError(err error) bool {
|
|||||||
}
|
}
|
||||||
msg := strings.ToLower(err.Error())
|
msg := strings.ToLower(err.Error())
|
||||||
nonRetryable := []string{
|
nonRetryable := []string{
|
||||||
"invalid_grant", // refresh_token 已失效
|
"invalid_grant", // refresh_token 已失效
|
||||||
"invalid_client", // 客户端配置错误
|
"refresh_token_reused", // OpenAI refresh_token 已被使用,必须重新授权
|
||||||
"unauthorized_client", // 客户端未授权
|
"invalid_client", // 客户端配置错误
|
||||||
"access_denied", // 访问被拒绝
|
"unauthorized_client", // 客户端未授权
|
||||||
"missing_project_id", // 缺少 project_id
|
"access_denied", // 访问被拒绝
|
||||||
|
"missing_project_id", // 缺少 project_id
|
||||||
"no refresh token available",
|
"no refresh token available",
|
||||||
}
|
}
|
||||||
for _, needle := range nonRetryable {
|
for _, needle := range nonRetryable {
|
||||||
|
|||||||
@ -532,6 +532,7 @@ func TestIsNonRetryableRefreshError(t *testing.T) {
|
|||||||
{name: "network_error", err: errors.New("network timeout"), expected: false},
|
{name: "network_error", err: errors.New("network timeout"), expected: false},
|
||||||
{name: "invalid_grant", err: errors.New("invalid_grant"), expected: true},
|
{name: "invalid_grant", err: errors.New("invalid_grant"), expected: true},
|
||||||
{name: "invalid_client", err: errors.New("invalid_client"), expected: true},
|
{name: "invalid_client", err: errors.New("invalid_client"), expected: true},
|
||||||
|
{name: "refresh_token_reused", err: errors.New(`OPENAI_OAUTH_TOKEN_REFRESH_FAILED: token refresh failed: status 401, body: {"error":{"code":"refresh_token_reused"}}`), expected: true},
|
||||||
{name: "unauthorized_client", err: errors.New("unauthorized_client"), expected: true},
|
{name: "unauthorized_client", err: errors.New("unauthorized_client"), expected: true},
|
||||||
{name: "access_denied", err: errors.New("access_denied"), expected: true},
|
{name: "access_denied", err: errors.New("access_denied"), expected: true},
|
||||||
{name: "no_refresh_token", err: errors.New("no refresh token available"), expected: true},
|
{name: "no_refresh_token", err: errors.New("no refresh token available"), expected: true},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user