feat: 新增支持codex转发
This commit is contained in:
155
frontend/src/composables/useOpenAIOAuth.ts
Normal file
155
frontend/src/composables/useOpenAIOAuth.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { ref } from 'vue'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
|
||||
export interface OpenAITokenInfo {
|
||||
access_token?: string
|
||||
refresh_token?: string
|
||||
id_token?: string
|
||||
token_type?: string
|
||||
expires_in?: number
|
||||
expires_at?: number
|
||||
scope?: string
|
||||
email?: string
|
||||
name?: string
|
||||
// OpenAI specific IDs (extracted from ID Token)
|
||||
chatgpt_account_id?: string
|
||||
chatgpt_user_id?: string
|
||||
organization_id?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export function useOpenAIOAuth() {
|
||||
const appStore = useAppStore()
|
||||
|
||||
// State
|
||||
const authUrl = ref('')
|
||||
const sessionId = ref('')
|
||||
const loading = ref(false)
|
||||
const error = ref('')
|
||||
|
||||
// Reset state
|
||||
const resetState = () => {
|
||||
authUrl.value = ''
|
||||
sessionId.value = ''
|
||||
loading.value = false
|
||||
error.value = ''
|
||||
}
|
||||
|
||||
// Generate auth URL for OpenAI OAuth
|
||||
const generateAuthUrl = async (
|
||||
proxyId?: number | null,
|
||||
redirectUri?: string
|
||||
): Promise<boolean> => {
|
||||
loading.value = true
|
||||
authUrl.value = ''
|
||||
sessionId.value = ''
|
||||
error.value = ''
|
||||
|
||||
try {
|
||||
const payload: Record<string, unknown> = {}
|
||||
if (proxyId) {
|
||||
payload.proxy_id = proxyId
|
||||
}
|
||||
if (redirectUri) {
|
||||
payload.redirect_uri = redirectUri
|
||||
}
|
||||
|
||||
const response = await adminAPI.accounts.generateAuthUrl('/admin/openai/generate-auth-url', payload)
|
||||
authUrl.value = response.auth_url
|
||||
sessionId.value = response.session_id
|
||||
return true
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || 'Failed to generate OpenAI auth URL'
|
||||
appStore.showError(error.value)
|
||||
return false
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Exchange auth code for tokens
|
||||
const exchangeAuthCode = async (
|
||||
code: string,
|
||||
currentSessionId: string,
|
||||
proxyId?: number | null
|
||||
): Promise<OpenAITokenInfo | null> => {
|
||||
if (!code.trim() || !currentSessionId) {
|
||||
error.value = 'Missing auth code or session ID'
|
||||
return null
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
|
||||
try {
|
||||
const payload: { session_id: string; code: string; proxy_id?: number } = {
|
||||
session_id: currentSessionId,
|
||||
code: code.trim()
|
||||
}
|
||||
if (proxyId) {
|
||||
payload.proxy_id = proxyId
|
||||
}
|
||||
|
||||
const tokenInfo = await adminAPI.accounts.exchangeCode('/admin/openai/exchange-code', payload)
|
||||
return tokenInfo as OpenAITokenInfo
|
||||
} catch (err: any) {
|
||||
error.value = err.response?.data?.detail || 'Failed to exchange OpenAI auth code'
|
||||
appStore.showError(error.value)
|
||||
return null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Build credentials for OpenAI OAuth account
|
||||
const buildCredentials = (tokenInfo: OpenAITokenInfo): Record<string, unknown> => {
|
||||
const creds: Record<string, unknown> = {
|
||||
access_token: tokenInfo.access_token,
|
||||
refresh_token: tokenInfo.refresh_token,
|
||||
token_type: tokenInfo.token_type,
|
||||
expires_in: tokenInfo.expires_in,
|
||||
expires_at: tokenInfo.expires_at,
|
||||
scope: tokenInfo.scope
|
||||
}
|
||||
|
||||
// Include OpenAI specific IDs (required for forwarding)
|
||||
if (tokenInfo.chatgpt_account_id) {
|
||||
creds.chatgpt_account_id = tokenInfo.chatgpt_account_id
|
||||
}
|
||||
if (tokenInfo.chatgpt_user_id) {
|
||||
creds.chatgpt_user_id = tokenInfo.chatgpt_user_id
|
||||
}
|
||||
if (tokenInfo.organization_id) {
|
||||
creds.organization_id = tokenInfo.organization_id
|
||||
}
|
||||
|
||||
return creds
|
||||
}
|
||||
|
||||
// Build extra info from token response
|
||||
const buildExtraInfo = (tokenInfo: OpenAITokenInfo): Record<string, string> | undefined => {
|
||||
const extra: Record<string, string> = {}
|
||||
if (tokenInfo.email) {
|
||||
extra.email = tokenInfo.email
|
||||
}
|
||||
if (tokenInfo.name) {
|
||||
extra.name = tokenInfo.name
|
||||
}
|
||||
return Object.keys(extra).length > 0 ? extra : undefined
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
authUrl,
|
||||
sessionId,
|
||||
loading,
|
||||
error,
|
||||
// Methods
|
||||
resetState,
|
||||
generateAuthUrl,
|
||||
exchangeAuthCode,
|
||||
buildCredentials,
|
||||
buildExtraInfo
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user