Commit c13c81f0 authored by QTom's avatar QTom
Browse files

feat(privacy): 为 OpenAI OAuth 账号添加前端手动设置隐私按钮



复用已有的 set-privacy API 端点,Handler 通过 platform 分发到
ForceOpenAIPrivacy / ForceAntigravityPrivacy,前端 AccountActionMenu
扩展隐私按钮支持 OpenAI OAuth 账号。
Co-Authored-By: default avatarClaude Opus 4.6 (1M context) <noreply@anthropic.com>
parent b688ebee
...@@ -1896,7 +1896,7 @@ func (h *AccountHandler) GetAvailableModels(c *gin.Context) { ...@@ -1896,7 +1896,7 @@ func (h *AccountHandler) GetAvailableModels(c *gin.Context) {
response.Success(c, models) 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 // POST /api/v1/admin/accounts/:id/set-privacy
func (h *AccountHandler) SetPrivacy(c *gin.Context) { func (h *AccountHandler) SetPrivacy(c *gin.Context) {
accountID, err := strconv.ParseInt(c.Param("id"), 10, 64) accountID, err := strconv.ParseInt(c.Param("id"), 10, 64)
...@@ -1909,11 +1909,20 @@ func (h *AccountHandler) SetPrivacy(c *gin.Context) { ...@@ -1909,11 +1909,20 @@ func (h *AccountHandler) SetPrivacy(c *gin.Context) {
response.NotFound(c, "Account not found") response.NotFound(c, "Account not found")
return return
} }
if account.Platform != service.PlatformAntigravity || account.Type != service.AccountTypeOAuth { if account.Type != service.AccountTypeOAuth {
response.BadRequest(c, "Only Antigravity OAuth accounts support privacy setting") 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 return
} }
mode := h.adminService.ForceAntigravityPrivacy(c.Request.Context(), account)
if mode == "" { if mode == "" {
response.BadRequest(c, "Cannot set privacy: missing access_token") response.BadRequest(c, "Cannot set privacy: missing access_token")
return return
......
...@@ -449,6 +449,10 @@ func (s *stubAdminService) EnsureAntigravityPrivacy(ctx context.Context, account ...@@ -449,6 +449,10 @@ func (s *stubAdminService) EnsureAntigravityPrivacy(ctx context.Context, account
return "" return ""
} }
func (s *stubAdminService) ForceOpenAIPrivacy(ctx context.Context, account *service.Account) string {
return ""
}
func (s *stubAdminService) ForceAntigravityPrivacy(ctx context.Context, account *service.Account) string { func (s *stubAdminService) ForceAntigravityPrivacy(ctx context.Context, account *service.Account) string {
return "" return ""
} }
......
...@@ -67,6 +67,8 @@ type AdminService interface { ...@@ -67,6 +67,8 @@ type AdminService interface {
EnsureOpenAIPrivacy(ctx context.Context, account *Account) string EnsureOpenAIPrivacy(ctx context.Context, account *Account) string
// EnsureAntigravityPrivacy 检查 Antigravity OAuth 账号 privacy_mode,未设置则调用 setUserSettings 并持久化。 // EnsureAntigravityPrivacy 检查 Antigravity OAuth 账号 privacy_mode,未设置则调用 setUserSettings 并持久化。
EnsureAntigravityPrivacy(ctx context.Context, account *Account) string EnsureAntigravityPrivacy(ctx context.Context, account *Account) string
// ForceOpenAIPrivacy 强制重新设置 OpenAI OAuth 账号隐私,无论当前状态。
ForceOpenAIPrivacy(ctx context.Context, account *Account) string
// ForceAntigravityPrivacy 强制重新设置 Antigravity OAuth 账号隐私,无论当前状态。 // ForceAntigravityPrivacy 强制重新设置 Antigravity OAuth 账号隐私,无论当前状态。
ForceAntigravityPrivacy(ctx context.Context, account *Account) string ForceAntigravityPrivacy(ctx context.Context, account *Account) string
SetAccountSchedulable(ctx context.Context, id int64, schedulable bool) (*Account, error) SetAccountSchedulable(ctx context.Context, id int64, schedulable bool) (*Account, error)
...@@ -2664,6 +2666,43 @@ func (s *adminServiceImpl) EnsureOpenAIPrivacy(ctx context.Context, account *Acc ...@@ -2664,6 +2666,43 @@ func (s *adminServiceImpl) EnsureOpenAIPrivacy(ctx context.Context, account *Acc
return mode 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 账号隐私状态。 // EnsureAntigravityPrivacy 检查 Antigravity OAuth 账号隐私状态。
// 如果 Extra["privacy_mode"] 已存在(无论成功或失败),直接跳过。 // 如果 Extra["privacy_mode"] 已存在(无论成功或失败),直接跳过。
// 仅对从未设置过隐私的账号执行 setUserSettings + fetchUserInfo 流程。 // 仅对从未设置过隐私的账号执行 setUserSettings + fetchUserInfo 流程。
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
{{ t('admin.accounts.refreshToken') }} {{ t('admin.accounts.refreshToken') }}
</button> </button>
</template> </template>
<button v-if="isAntigravityOAuth" @click="$emit('set-privacy', account); $emit('close')" class="flex w-full items-center gap-2 px-4 py-2 text-sm text-emerald-600 hover:bg-gray-100 dark:hover:bg-dark-700"> <button v-if="supportsPrivacy" @click="$emit('set-privacy', account); $emit('close')" class="flex w-full items-center gap-2 px-4 py-2 text-sm text-emerald-600 hover:bg-gray-100 dark:hover:bg-dark-700">
<Icon name="shield" size="sm" /> <Icon name="shield" size="sm" />
{{ t('admin.accounts.setPrivacy') }} {{ t('admin.accounts.setPrivacy') }}
</button> </button>
...@@ -80,6 +80,8 @@ const hasRecoverableState = computed(() => { ...@@ -80,6 +80,8 @@ const hasRecoverableState = computed(() => {
return props.account?.status === 'error' || Boolean(isRateLimited.value) || Boolean(isOverloaded.value) || Boolean(isTempUnschedulable.value) 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 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(() => { const hasQuotaLimit = computed(() => {
return (props.account?.type === 'apikey' || props.account?.type === 'bedrock') && ( return (props.account?.type === 'apikey' || props.account?.type === 'bedrock') && (
(props.account?.quota_limit ?? 0) > 0 || (props.account?.quota_limit ?? 0) > 0 ||
......
...@@ -1262,7 +1262,7 @@ const handleSetPrivacy = async (a: Account) => { ...@@ -1262,7 +1262,7 @@ const handleSetPrivacy = async (a: Account) => {
appStore.showSuccess(t('common.success')) appStore.showSuccess(t('common.success'))
} catch (error: any) { } catch (error: any) {
console.error('Failed to set privacy:', error) 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 } const handleDelete = (a: Account) => { deletingAcc.value = a; showDeleteDialog.value = true }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment