diff --git a/backend/internal/handler/admin/account_handler.go b/backend/internal/handler/admin/account_handler.go index 6711abae..edcd9976 100644 --- a/backend/internal/handler/admin/account_handler.go +++ b/backend/internal/handler/admin/account_handler.go @@ -1896,7 +1896,7 @@ func (h *AccountHandler) GetAvailableModels(c *gin.Context) { response.Success(c, models) } -// SetPrivacy handles setting privacy for a single Antigravity OAuth account +// SetPrivacy handles setting privacy for a single OpenAI/Antigravity OAuth account // POST /api/v1/admin/accounts/:id/set-privacy func (h *AccountHandler) SetPrivacy(c *gin.Context) { accountID, err := strconv.ParseInt(c.Param("id"), 10, 64) @@ -1909,11 +1909,20 @@ func (h *AccountHandler) SetPrivacy(c *gin.Context) { response.NotFound(c, "Account not found") return } - if account.Platform != service.PlatformAntigravity || account.Type != service.AccountTypeOAuth { - response.BadRequest(c, "Only Antigravity OAuth accounts support privacy setting") + if account.Type != service.AccountTypeOAuth { + response.BadRequest(c, "Only OAuth accounts support privacy setting") + return + } + var mode string + switch account.Platform { + case service.PlatformOpenAI: + mode = h.adminService.ForceOpenAIPrivacy(c.Request.Context(), account) + case service.PlatformAntigravity: + mode = h.adminService.ForceAntigravityPrivacy(c.Request.Context(), account) + default: + response.BadRequest(c, "Only OpenAI and Antigravity OAuth accounts support privacy setting") return } - mode := h.adminService.ForceAntigravityPrivacy(c.Request.Context(), account) if mode == "" { response.BadRequest(c, "Cannot set privacy: missing access_token") return diff --git a/backend/internal/handler/admin/admin_service_stub_test.go b/backend/internal/handler/admin/admin_service_stub_test.go index 745c5610..9759cef5 100644 --- a/backend/internal/handler/admin/admin_service_stub_test.go +++ b/backend/internal/handler/admin/admin_service_stub_test.go @@ -449,6 +449,10 @@ func (s *stubAdminService) EnsureAntigravityPrivacy(ctx context.Context, account return "" } +func (s *stubAdminService) ForceOpenAIPrivacy(ctx context.Context, account *service.Account) string { + return "" +} + func (s *stubAdminService) ForceAntigravityPrivacy(ctx context.Context, account *service.Account) string { return "" } diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go index d028cc22..fa175d5d 100644 --- a/backend/internal/service/admin_service.go +++ b/backend/internal/service/admin_service.go @@ -67,6 +67,8 @@ type AdminService interface { EnsureOpenAIPrivacy(ctx context.Context, account *Account) string // EnsureAntigravityPrivacy 检查 Antigravity OAuth 账号 privacy_mode,未设置则调用 setUserSettings 并持久化。 EnsureAntigravityPrivacy(ctx context.Context, account *Account) string + // ForceOpenAIPrivacy 强制重新设置 OpenAI OAuth 账号隐私,无论当前状态。 + ForceOpenAIPrivacy(ctx context.Context, account *Account) string // ForceAntigravityPrivacy 强制重新设置 Antigravity OAuth 账号隐私,无论当前状态。 ForceAntigravityPrivacy(ctx context.Context, account *Account) string SetAccountSchedulable(ctx context.Context, id int64, schedulable bool) (*Account, error) @@ -2664,6 +2666,43 @@ func (s *adminServiceImpl) EnsureOpenAIPrivacy(ctx context.Context, account *Acc return mode } +// ForceOpenAIPrivacy 强制重新设置 OpenAI OAuth 账号隐私,无论当前状态。 +func (s *adminServiceImpl) ForceOpenAIPrivacy(ctx context.Context, account *Account) string { + if account.Platform != PlatformOpenAI || account.Type != AccountTypeOAuth { + return "" + } + if s.privacyClientFactory == nil { + return "" + } + + token, _ := account.Credentials["access_token"].(string) + if token == "" { + return "" + } + + var proxyURL string + if account.ProxyID != nil { + if p, err := s.proxyRepo.GetByID(ctx, *account.ProxyID); err == nil && p != nil { + proxyURL = p.URL() + } + } + + mode := disableOpenAITraining(ctx, s.privacyClientFactory, token, proxyURL) + if mode == "" { + return "" + } + + if err := s.accountRepo.UpdateExtra(ctx, account.ID, map[string]any{"privacy_mode": mode}); err != nil { + logger.LegacyPrintf("service.admin", "force_update_openai_privacy_mode_failed: account_id=%d err=%v", account.ID, err) + return mode + } + if account.Extra == nil { + account.Extra = make(map[string]any) + } + account.Extra["privacy_mode"] = mode + return mode +} + // EnsureAntigravityPrivacy 检查 Antigravity OAuth 账号隐私状态。 // 如果 Extra["privacy_mode"] 已存在(无论成功或失败),直接跳过。 // 仅对从未设置过隐私的账号执行 setUserSettings + fetchUserInfo 流程。 diff --git a/frontend/src/components/admin/account/AccountActionMenu.vue b/frontend/src/components/admin/account/AccountActionMenu.vue index e682ddaf..06bd23ab 100644 --- a/frontend/src/components/admin/account/AccountActionMenu.vue +++ b/frontend/src/components/admin/account/AccountActionMenu.vue @@ -32,7 +32,7 @@ {{ t('admin.accounts.refreshToken') }} - @@ -80,6 +80,8 @@ const hasRecoverableState = computed(() => { return props.account?.status === 'error' || Boolean(isRateLimited.value) || Boolean(isOverloaded.value) || Boolean(isTempUnschedulable.value) }) const isAntigravityOAuth = computed(() => props.account?.platform === 'antigravity' && props.account?.type === 'oauth') +const isOpenAIOAuth = computed(() => props.account?.platform === 'openai' && props.account?.type === 'oauth') +const supportsPrivacy = computed(() => isAntigravityOAuth.value || isOpenAIOAuth.value) const hasQuotaLimit = computed(() => { return (props.account?.type === 'apikey' || props.account?.type === 'bedrock') && ( (props.account?.quota_limit ?? 0) > 0 || diff --git a/frontend/src/views/admin/AccountsView.vue b/frontend/src/views/admin/AccountsView.vue index 85f27d69..35e0fcec 100644 --- a/frontend/src/views/admin/AccountsView.vue +++ b/frontend/src/views/admin/AccountsView.vue @@ -1262,7 +1262,7 @@ const handleSetPrivacy = async (a: Account) => { appStore.showSuccess(t('common.success')) } catch (error: any) { console.error('Failed to set privacy:', error) - appStore.showError(error?.response?.data?.message || t('admin.accounts.privacyAntigravityFailed')) + appStore.showError(error?.response?.data?.message || t('admin.accounts.privacyFailed')) } } const handleDelete = (a: Account) => { deletingAcc.value = a; showDeleteDialog.value = true }