From 64850811224ea421caceb2f20fa36b3047372f37 Mon Sep 17 00:00:00 2001 From: cat Date: Tue, 14 Jul 2026 19:25:13 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E6=A0=87=E6=98=8E=20OpenAI?= =?UTF-8?q?=20=E8=AE=A4=E8=AF=81=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/OAuthAuthorizationFlow.vue | 2 +- .../components/common/PlatformTypeBadge.vue | 9 ++++++++ .../__tests__/PlatformTypeBadge.grok.spec.ts | 21 +++++++++++++++++++ .../src/i18n/locales/en/admin/accounts.ts | 12 +++++------ .../src/i18n/locales/zh/admin/accounts.ts | 12 +++++------ frontend/src/views/admin/AccountsView.vue | 7 +++++++ 6 files changed, 50 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/account/OAuthAuthorizationFlow.vue b/frontend/src/components/account/OAuthAuthorizationFlow.vue index a23ec798d2..395b4ae923 100644 --- a/frontend/src/components/account/OAuthAuthorizationFlow.vue +++ b/frontend/src/components/account/OAuthAuthorizationFlow.vue @@ -276,7 +276,7 @@ - +
{ return 'Gemini' }) +const normalizedAuthMode = computed(() => + (props.authMode || '').trim().toLowerCase().replace(/[\s_-]+/g, '') +) + const typeLabel = computed(() => { + if (props.platform === 'openai' && props.type === 'oauth') { + if (normalizedAuthMode.value === 'agentidentity') return 'Agent Identity' + if (normalizedAuthMode.value === 'personalaccesstoken') return 'PAT' + } switch (props.type) { case 'oauth': return 'OAuth' diff --git a/frontend/src/components/common/__tests__/PlatformTypeBadge.grok.spec.ts b/frontend/src/components/common/__tests__/PlatformTypeBadge.grok.spec.ts index 753fb1a5f2..0bad3d9551 100644 --- a/frontend/src/components/common/__tests__/PlatformTypeBadge.grok.spec.ts +++ b/frontend/src/components/common/__tests__/PlatformTypeBadge.grok.spec.ts @@ -62,3 +62,24 @@ describe('PlatformTypeBadge Grok plans', () => { expect(wrapper.findAll('path')).toHaveLength(2) }) }) + +describe('PlatformTypeBadge OpenAI authentication modes', () => { + it('distinguishes Agent Identity, PAT, and OAuth accounts', async () => { + const wrapper = mount(PlatformTypeBadge, { + props: { + platform: 'openai', + type: 'oauth', + authMode: 'agentIdentity', + }, + }) + + expect(wrapper.text()).toContain('Agent Identity') + + await wrapper.setProps({ authMode: 'personalAccessToken' }) + expect(wrapper.text()).toContain('PAT') + expect(wrapper.text()).not.toContain('Agent Identity') + + await wrapper.setProps({ authMode: undefined }) + expect(wrapper.text()).toContain('OAuth') + }) +}) diff --git a/frontend/src/i18n/locales/en/admin/accounts.ts b/frontend/src/i18n/locales/en/admin/accounts.ts index e64a51ea20..64a86dad17 100644 --- a/frontend/src/i18n/locales/en/admin/accounts.ts +++ b/frontend/src/i18n/locales/en/admin/accounts.ts @@ -822,13 +822,13 @@ export default { refreshTokenAuth: 'Manual RT Input', refreshTokenDesc: 'Enter your existing OpenAI Refresh Token(s). Supports batch input (one per line). The system will automatically validate and create accounts.', refreshTokenPlaceholder: 'Paste your OpenAI Refresh Token...\nSupports multiple, one per line', - codexSessionAuth: 'Codex JSON / AT Batch Input', - codexSessionDesc: 'Paste Codex JSON or an accessToken. Accounts use the step 1 settings.', - codexSessionInputLabel: 'Codex JSON or accessToken', - codexSessionPlaceholder: 'Multiple lines supported, one token or JSON per line', - codexSessionHint: 'sessionToken will not be saved as refresh_token. Without refresh_token, the account expires with the accessToken expiry; import is rejected if the expiry cannot be parsed and step 1 has no expiration.', + codexSessionAuth: 'Codex auth.json / AT Import', + codexSessionDesc: 'Paste a Codex auth.json (OAuth or Agent Identity) or an accessToken. Accounts use the step 1 settings.', + codexSessionInputLabel: 'Codex auth.json or accessToken', + codexSessionPlaceholder: 'Multiple lines supported, one token or auth.json object per line', + codexSessionHint: 'Agent Identity keeps no OAuth token and signs each upstream request dynamically. Session/access-token imports retain their existing expiration behavior.', codexSessionImportAndCreate: 'Import & Create Account', - codexSessionEmpty: 'Please enter Codex JSON or accessToken', + codexSessionEmpty: 'Please enter a Codex auth.json or accessToken', codexSessionImportFailed: 'Failed to import Codex account', codexSessionImportSuccess: 'Import completed: created {created}, updated {updated}, skipped {skipped}', codexSessionImportPartial: 'Partial success: created {created}, updated {updated}, skipped {skipped}, failed {failed}', diff --git a/frontend/src/i18n/locales/zh/admin/accounts.ts b/frontend/src/i18n/locales/zh/admin/accounts.ts index 49d4b8564f..65fb089acd 100644 --- a/frontend/src/i18n/locales/zh/admin/accounts.ts +++ b/frontend/src/i18n/locales/zh/admin/accounts.ts @@ -909,13 +909,13 @@ export default { refreshTokenAuth: '手动输入 RT', refreshTokenDesc: '输入您已有的 OpenAI Refresh Token,支持批量输入(每行一个),系统将自动验证并创建账号。', refreshTokenPlaceholder: '粘贴您的 OpenAI Refresh Token...\n支持多个,每行一个', - codexSessionAuth: 'Codex JSON / AT 批量输入', - codexSessionDesc: '粘贴 Codex JSON 或 accessToken,按第一步配置创建账号。', - codexSessionInputLabel: 'Codex JSON 或 accessToken', - codexSessionPlaceholder: '支持多行,每行一个 token 或 JSON', - codexSessionHint: 'sessionToken 不会作为 refresh_token 保存;未包含 refresh_token 时会按 accessToken 过期时间设置账号过期,无法解析且第一步未设置过期时间时会拒绝导入。', + codexSessionAuth: 'Codex auth.json / AT 导入', + codexSessionDesc: '粘贴 Codex auth.json(OAuth 或 Agent Identity)或 accessToken,按第一步配置创建账号。', + codexSessionInputLabel: 'Codex auth.json 或 accessToken', + codexSessionPlaceholder: '支持多行,每行一个 token 或 auth.json 对象', + codexSessionHint: 'Agent Identity 不保存 OAuth token,并为每次上游请求动态签名;session/accessToken 导入继续沿用原有过期规则。', codexSessionImportAndCreate: '导入并创建账号', - codexSessionEmpty: '请输入 Codex JSON 或 accessToken', + codexSessionEmpty: '请输入 Codex auth.json 或 accessToken', codexSessionImportFailed: 'Codex 账号导入失败', codexSessionImportSuccess: '导入完成:新增 {created},更新 {updated},跳过 {skipped}', codexSessionImportPartial: '部分成功:新增 {created},更新 {updated},跳过 {skipped},失败 {failed}', diff --git a/frontend/src/views/admin/AccountsView.vue b/frontend/src/views/admin/AccountsView.vue index e8bac39af4..140b7c934f 100644 --- a/frontend/src/views/admin/AccountsView.vue +++ b/frontend/src/views/admin/AccountsView.vue @@ -234,6 +234,7 @@
@@ -1167,6 +1168,12 @@ function getAccountPlanType(row: any): string | undefined { return row.credentials?.plan_type || row.parent_plan_type || undefined } +function getOpenAIAuthMode(row: any): string | undefined { + if (!row || row.platform !== 'openai' || row.type !== 'oauth') return undefined + const authMode = row.credentials?.auth_mode + return typeof authMode === 'string' && authMode.trim() ? authMode : undefined +} + // Antigravity 订阅等级辅助函数 function getAntigravityTierFromRow(row: any): string | null { if (row.platform !== 'antigravity') return null