From 2bbf6031482a4e7737f1609880d1a7eb89e247d5 Mon Sep 17 00:00:00 2001 From: "CJACK." Date: Wed, 18 Mar 2026 00:52:24 +0800 Subject: [PATCH] fix: address PR #97 review findings --- internal/adapter/openai/handler_chat.go | 6 +- internal/admin/handler_accounts_testing.go | 14 ++++- .../admin/handler_accounts_testing_test.go | 57 +++++++++++++++++-- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/internal/adapter/openai/handler_chat.go b/internal/adapter/openai/handler_chat.go index c13e75c..c514e36 100644 --- a/internal/adapter/openai/handler_chat.go +++ b/internal/adapter/openai/handler_chat.go @@ -42,7 +42,9 @@ func (h *Handler) ChatCompletions(w http.ResponseWriter, r *http.Request) { // 2. 新请求可能获取到同一账号并开始使用 // 3. 异步删除仍在进行,会截断新请求正在使用的会话 if h.Store.AutoDeleteSessions() && a.DeepSeekToken != "" { - err := h.DS.DeleteAllSessionsForToken(context.Background(), a.DeepSeekToken) + deleteCtx, cancel := context.WithTimeout(r.Context(), 10*time.Second) + defer cancel() + err := h.DS.DeleteAllSessionsForToken(deleteCtx, a.DeepSeekToken) if err != nil { config.Logger.Warn("[auto_delete_sessions] failed", "account", a.AccountID, "error", err) } else { @@ -51,7 +53,7 @@ func (h *Handler) ChatCompletions(w http.ResponseWriter, r *http.Request) { } h.Auth.Release(a) }() - + r = r.WithContext(auth.WithAuth(r.Context(), a)) var req map[string]any diff --git a/internal/admin/handler_accounts_testing.go b/internal/admin/handler_accounts_testing.go index 0dc602d..93b0c8d 100644 --- a/internal/admin/handler_accounts_testing.go +++ b/internal/admin/handler_accounts_testing.go @@ -247,8 +247,18 @@ func (h *Handler) deleteAllSessions(w http.ResponseWriter, r *http.Request) { // 删除所有会话 err := h.DS.DeleteAllSessionsForToken(r.Context(), token) if err != nil { - writeJSON(w, http.StatusOK, map[string]any{"success": false, "message": "删除失败: " + err.Error()}) - return + // token 可能过期,尝试重新登录并重试一次 + newToken, loginErr := h.DS.Login(r.Context(), acc) + if loginErr != nil { + writeJSON(w, http.StatusOK, map[string]any{"success": false, "message": "删除失败: " + err.Error()}) + return + } + token = newToken + _ = h.Store.UpdateAccountToken(acc.Identifier(), token) + if retryErr := h.DS.DeleteAllSessionsForToken(r.Context(), token); retryErr != nil { + writeJSON(w, http.StatusOK, map[string]any{"success": false, "message": "删除失败: " + retryErr.Error()}) + return + } } writeJSON(w, http.StatusOK, map[string]any{"success": true, "message": "删除成功"}) diff --git a/internal/admin/handler_accounts_testing_test.go b/internal/admin/handler_accounts_testing_test.go index 1636669..e80eefe 100644 --- a/internal/admin/handler_accounts_testing_test.go +++ b/internal/admin/handler_accounts_testing_test.go @@ -1,9 +1,12 @@ package admin import ( + "bytes" "context" + "encoding/json" "errors" "net/http" + "net/http/httptest" "strings" "testing" @@ -13,10 +16,13 @@ import ( ) type testingDSMock struct { - loginCalls int - createSessionCalls int - getPowCalls int - callCompletionCalls int + loginCalls int + createSessionCalls int + getPowCalls int + callCompletionCalls int + deleteAllSessionsCalls int + deleteAllSessionsError error + deleteAllSessionsErrorOnce bool } func (m *testingDSMock) Login(_ context.Context, _ config.Account) (string, error) { @@ -40,6 +46,14 @@ func (m *testingDSMock) CallCompletion(_ context.Context, _ *auth.RequestAuth, _ } func (m *testingDSMock) DeleteAllSessionsForToken(_ context.Context, _ string) error { + m.deleteAllSessionsCalls++ + if m.deleteAllSessionsError != nil { + err := m.deleteAllSessionsError + if m.deleteAllSessionsErrorOnce { + m.deleteAllSessionsError = nil + } + return err + } return nil } @@ -83,3 +97,38 @@ func TestTestAccount_BatchModeOnlyCreatesSession(t *testing.T) { t.Fatalf("expected test status ok, got %q", updated.TestStatus) } } + +func TestDeleteAllSessions_RetryWithReloginOnDeleteFailure(t *testing.T) { + t.Setenv("DS2API_CONFIG_JSON", `{"accounts":[{"email":"batch@example.com","password":"pwd","token":"expired-token"}]}`) + store := config.LoadStore() + ds := &testingDSMock{deleteAllSessionsError: errors.New("token expired"), deleteAllSessionsErrorOnce: true} + h := &Handler{Store: store, DS: ds} + + req := httptest.NewRequest(http.MethodPost, "/delete-all", bytes.NewBufferString(`{"identifier":"batch@example.com"}`)) + rec := httptest.NewRecorder() + h.deleteAllSessions(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("expected status 200, got %d", rec.Code) + } + var resp map[string]any + if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { + t.Fatalf("unmarshal response: %v", err) + } + if ok, _ := resp["success"].(bool); !ok { + t.Fatalf("expected success response, got %#v", resp) + } + if ds.loginCalls != 1 { + t.Fatalf("expected relogin once, got %d", ds.loginCalls) + } + if ds.deleteAllSessionsCalls != 2 { + t.Fatalf("expected delete called twice, got %d", ds.deleteAllSessionsCalls) + } + updated, ok := store.FindAccount("batch@example.com") + if !ok { + t.Fatal("expected account") + } + if updated.Token != "new-token" { + t.Fatalf("expected refreshed token persisted, got %q", updated.Token) + } +}