fix(sora): add GDrive OAuth callback page for postMessage flow

The OAuth flow opens a popup window that redirects to /admin/gdrive-oauth-callback,
but this route and page were missing. Add the callback view that extracts the
authorization code from the URL and sends it back to the opener window via
postMessage, completing the OAuth authorization flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
erio
2026-03-06 21:10:02 +08:00
co-authored by Claude Opus 4.6
parent e0a9c34067
commit e3f9774eda
4 changed files with 88 additions and 2 deletions
+3 -1
View File
@@ -3974,7 +3974,9 @@ export default {
authorizeHint: 'Get Refresh Token via OAuth2',
oauthFieldsRequired: 'Please fill in Client ID and Client Secret first',
oauthSuccess: 'Google Drive authorization successful',
oauthFailed: 'Google Drive authorization failed'
oauthFailed: 'Google Drive authorization failed',
closeWindow: 'This window will close automatically',
processing: 'Processing authorization...'
}
},
streamTimeout: {
+3 -1
View File
@@ -4134,7 +4134,9 @@ export default {
authorizeHint: '通过 OAuth2 获取 Refresh Token',
oauthFieldsRequired: '请先填写 Client ID 和 Client Secret',
oauthSuccess: 'Google Drive 授权成功',
oauthFailed: 'Google Drive 授权失败'
oauthFailed: 'Google Drive 授权失败',
closeWindow: '此窗口将自动关闭',
processing: '正在处理授权...'
}
},
streamTimeout: {
+11
View File
@@ -225,6 +225,17 @@ const routes: RouteRecordRaw[] = [
}
},
// ==================== Admin OAuth Callback Routes ====================
{
path: '/admin/gdrive-oauth-callback',
name: 'GDriveOAuthCallback',
component: () => import('@/views/admin/GDriveOAuthCallbackView.vue'),
meta: {
requiresAuth: false,
title: 'Google Drive OAuth'
}
},
// ==================== Admin Routes ====================
{
path: '/admin',
@@ -0,0 +1,71 @@
<template>
<div class="flex min-h-screen items-center justify-center bg-gray-50 px-4 dark:bg-dark-900">
<div class="w-full max-w-md text-center">
<div v-if="error" class="card p-8">
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-red-100 dark:bg-red-900/30">
<svg class="h-6 w-6 text-red-600 dark:text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">{{ t('admin.settings.soraS3.gdrive.oauthFailed') }}</h2>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">{{ error }}</p>
<p class="mt-4 text-xs text-gray-400 dark:text-gray-500">{{ t('admin.settings.soraS3.gdrive.closeWindow') }}</p>
</div>
<div v-else-if="sent" class="card p-8">
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-green-100 dark:bg-green-900/30">
<svg class="h-6 w-6 text-green-600 dark:text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">{{ t('admin.settings.soraS3.gdrive.oauthSuccess') }}</h2>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">{{ t('admin.settings.soraS3.gdrive.closeWindow') }}</p>
</div>
<div v-else class="card p-8">
<div class="mx-auto mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500"></div>
<p class="text-sm text-gray-500 dark:text-gray-400">{{ t('admin.settings.soraS3.gdrive.processing') }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute } from 'vue-router'
const route = useRoute()
const { t } = useI18n()
const error = ref('')
const sent = ref(false)
onMounted(() => {
const code = route.query.code as string
const state = route.query.state as string
const errorParam = route.query.error as string
const errorDesc = route.query.error_description as string
if (errorParam) {
error.value = errorDesc || errorParam
return
}
if (!code) {
error.value = 'No authorization code received'
return
}
// Send code back to opener window via postMessage
if (window.opener) {
window.opener.postMessage(
{ type: 'gdrive_oauth_callback', code, state },
window.location.origin
)
sent.value = true
// Auto-close after a short delay
setTimeout(() => window.close(), 1500)
} else {
error.value = 'Cannot communicate with the parent window. Please close this window and try again.'
}
})
</script>