| import { ref } from 'vue' |
| import { useAppStore } from '@/stores/app' |
| import { adminAPI } from '@/api/admin' |
|
|
| export interface OpenAITokenInfo { |
| access_token?: string |
| refresh_token?: string |
| client_id?: string |
| id_token?: string |
| token_type?: string |
| expires_in?: number |
| expires_at?: number |
| scope?: string |
| email?: string |
| name?: string |
| |
| chatgpt_account_id?: string |
| chatgpt_user_id?: string |
| organization_id?: string |
| [key: string]: unknown |
| } |
|
|
| export type OpenAIOAuthPlatform = 'openai' | 'sora' |
|
|
| interface UseOpenAIOAuthOptions { |
| platform?: OpenAIOAuthPlatform |
| } |
|
|
| export function useOpenAIOAuth(options?: UseOpenAIOAuthOptions) { |
| const appStore = useAppStore() |
| const oauthPlatform = options?.platform ?? 'openai' |
| const endpointPrefix = oauthPlatform === 'sora' ? '/admin/sora' : '/admin/openai' |
|
|
| |
| const authUrl = ref('') |
| const sessionId = ref('') |
| const oauthState = ref('') |
| const loading = ref(false) |
| const error = ref('') |
|
|
| |
| const resetState = () => { |
| authUrl.value = '' |
| sessionId.value = '' |
| oauthState.value = '' |
| loading.value = false |
| error.value = '' |
| } |
|
|
| |
| const generateAuthUrl = async ( |
| proxyId?: number | null, |
| redirectUri?: string |
| ): Promise<boolean> => { |
| loading.value = true |
| authUrl.value = '' |
| sessionId.value = '' |
| oauthState.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( |
| `${endpointPrefix}/generate-auth-url`, |
| payload |
| ) |
| authUrl.value = response.auth_url |
| sessionId.value = response.session_id |
| try { |
| const parsed = new URL(response.auth_url) |
| oauthState.value = parsed.searchParams.get('state') || '' |
| } catch { |
| oauthState.value = '' |
| } |
| 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 |
| } |
| } |
|
|
| |
| const exchangeAuthCode = async ( |
| code: string, |
| currentSessionId: string, |
| state: string, |
| proxyId?: number | null |
| ): Promise<OpenAITokenInfo | null> => { |
| if (!code.trim() || !currentSessionId || !state.trim()) { |
| error.value = 'Missing auth code, session ID, or state' |
| return null |
| } |
|
|
| loading.value = true |
| error.value = '' |
|
|
| try { |
| const payload: { session_id: string; code: string; state: string; proxy_id?: number } = { |
| session_id: currentSessionId, |
| code: code.trim(), |
| state: state.trim() |
| } |
| if (proxyId) { |
| payload.proxy_id = proxyId |
| } |
|
|
| const tokenInfo = await adminAPI.accounts.exchangeCode(`${endpointPrefix}/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 |
| } |
| } |
|
|
| |
| const validateRefreshToken = async ( |
| refreshToken: string, |
| proxyId?: number | null |
| ): Promise<OpenAITokenInfo | null> => { |
| if (!refreshToken.trim()) { |
| error.value = 'Missing refresh token' |
| return null |
| } |
|
|
| loading.value = true |
| error.value = '' |
|
|
| try { |
| |
| const tokenInfo = await adminAPI.accounts.refreshOpenAIToken( |
| refreshToken.trim(), |
| proxyId, |
| `${endpointPrefix}/refresh-token` |
| ) |
| return tokenInfo as OpenAITokenInfo |
| } catch (err: any) { |
| error.value = err.response?.data?.detail || 'Failed to validate refresh token' |
| appStore.showError(error.value) |
| return null |
| } finally { |
| loading.value = false |
| } |
| } |
|
|
| |
| const validateSessionToken = async ( |
| sessionToken: string, |
| proxyId?: number | null |
| ): Promise<OpenAITokenInfo | null> => { |
| if (!sessionToken.trim()) { |
| error.value = 'Missing session token' |
| return null |
| } |
| loading.value = true |
| error.value = '' |
| try { |
| const tokenInfo = await adminAPI.accounts.validateSoraSessionToken( |
| sessionToken.trim(), |
| proxyId, |
| `${endpointPrefix}/st2at` |
| ) |
| return tokenInfo as OpenAITokenInfo |
| } catch (err: any) { |
| error.value = err.response?.data?.detail || 'Failed to validate session token' |
| appStore.showError(error.value) |
| return null |
| } finally { |
| loading.value = false |
| } |
| } |
|
|
| |
| 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 |
| } |
|
|
| if (tokenInfo.client_id) { |
| creds.client_id = tokenInfo.client_id |
| } |
|
|
| |
| 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 |
| } |
|
|
| |
| 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 { |
| |
| authUrl, |
| sessionId, |
| oauthState, |
| loading, |
| error, |
| |
| resetState, |
| generateAuthUrl, |
| exchangeAuthCode, |
| validateRefreshToken, |
| validateSessionToken, |
| buildCredentials, |
| buildExtraInfo |
| } |
| } |
|
|