import { NextRequest, NextResponse } from 'next/server'; import type { OAuthCallbackParams } from '@/app/types/google-oauth'; /** * Google OAuth回调处理API * 处理从Google OAuth返回的授权码,完成用户认证 */ export async function POST(request: NextRequest) { try { const body = await request.json(); const { code, state, inviteCode } = body; // 验证必需参数 if (!code || !state) { return NextResponse.json( { success: false, message: 'Missing required parameters: code and state' }, { status: 400 } ); } // 开发模式:使用测试环境的OAuth处理 const isDevelopment = process.env.NODE_ENV === 'development'; const useTestEnv = isDevelopment; // 开发环境默认使用测试环境 if (useTestEnv) { console.log('🧪 开发模式:使用模拟OAuth响应'); // 解析state参数获取origin信息 let stateData: any = {}; try { stateData = JSON.parse(state); } catch (e) { console.warn('无法解析state参数:', state); } // 模拟成功的OAuth响应 const mockResponse = { success: true, data: { token: 'dev-mock-token-' + Date.now(), user: { userId: 'dev-user-' + Math.random().toString(36).substr(2, 9), userName: 'Development User', name: 'Dev User', email: 'dev@movieflow.com', authType: 'GOOGLE', isNewUser: false }, userInfo: { userId: 'dev-user-' + Math.random().toString(36).substr(2, 9), userName: 'Development User', name: 'Dev User', email: 'dev@movieflow.com', authType: 'GOOGLE', isNewUser: false }, message: 'Development mode - Google authentication simulated' } }; console.log('返回模拟OAuth响应:', mockResponse); return NextResponse.json(mockResponse); } // 解析state参数 let stateData: any = {}; try { stateData = JSON.parse(state); } catch (e) { console.warn('无法解析state参数:', state); return NextResponse.json( { success: false, message: 'Invalid state parameter' }, { status: 400 } ); } console.log('Google OAuth回调处理开始:', { codeLength: code.length, stateData, inviteCode }); // 第一步:使用authorization code向Google换取access token和id_token const googleClientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '847079918888-o1nne8d3ij80dn20qurivo987pv07225.apps.googleusercontent.com'; const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET || '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'; if (!googleClientSecret) { console.error('Google Client Secret未配置'); return NextResponse.json( { success: false, message: 'Google Client Secret not configured' }, { status: 500 } ); } console.log('开始向Google交换token...'); // 创建fetch配置,包含超时和重试机制 const fetchOptions = { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ code, client_id: googleClientId, client_secret: googleClientSecret, redirect_uri: getRedirectUri(request), grant_type: 'authorization_code', }), // 增加超时时间到30秒 signal: AbortSignal.timeout(30000) }; let tokenResponse; try { tokenResponse = await fetch('https://oauth2.googleapis.com/token', fetchOptions); } catch (fetchError: any) { console.error('Google API连接失败:', fetchError.message); // 如果是超时错误,提供更友好的错误信息 if (fetchError.name === 'TimeoutError' || fetchError.code === 'UND_ERR_CONNECT_TIMEOUT') { return NextResponse.json( { success: false, message: 'Google authentication service is temporarily unavailable. Please check your network connection and try again.' }, { status: 503 } ); } throw fetchError; } console.log('Google token exchange响应状态:', tokenResponse.status); if (!tokenResponse.ok) { const errorText = await tokenResponse.text(); console.error('Google token exchange failed:', errorText); return NextResponse.json( { success: false, message: 'Failed to exchange authorization code for tokens' }, { status: 400 } ); } const tokenData = await tokenResponse.json(); console.log('Google token exchange成功,获得token'); const { id_token } = tokenData; if (!id_token) { console.error('No id_token received from Google'); return NextResponse.json( { success: false, message: 'No id_token received from Google' }, { status: 400 } ); } // 第二步:使用id_token调用Java后端 const javaBaseUrl = process.env.NEXT_PUBLIC_JAVA_URL || 'https://auth.test.movieflow.ai'; console.log('开始调用Java后端:', javaBaseUrl); const backendResponse = await fetch(`${javaBaseUrl}/api/auth/google/login`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({ idToken: id_token, // 使用从Google获取的id_token action: 'auto', // 自动判断登录或注册 inviteCode: inviteCode || stateData.inviteCode || undefined }) }); console.log('Java后端响应状态:', backendResponse.status); const backendResult = await backendResponse.json(); if (!backendResponse.ok || !backendResult.success) { console.error('Java后端处理Google OAuth失败:', backendResult); return NextResponse.json( { success: false, message: backendResult.message || 'Google authentication failed' }, { status: backendResponse.status || 500 } ); } console.log('Google OAuth认证成功:', { userId: backendResult.data?.user?.userId, email: backendResult.data?.user?.email }); // 返回成功结果 return NextResponse.json({ success: true, data: { token: backendResult.data.token, user: backendResult.data.userInfo || backendResult.data.user, message: 'Google authentication successful' } }); } catch (error: any) { console.error('Google OAuth回调处理错误:', error); // 检查是否是网络连接错误 if (error.code === 'ECONNREFUSED' || error.message?.includes('fetch failed')) { console.error('网络连接失败,可能是Java后端服务不可用'); return NextResponse.json( { success: false, message: 'Backend service unavailable. Please try again later.' }, { status: 503 } ); } return NextResponse.json( { success: false, message: error.message || 'Internal server error during Google OAuth callback' }, { status: 500 } ); } } /** * 根据请求获取正确的redirect_uri */ function getRedirectUri(request: NextRequest): string { const host = request.headers.get('host') || ''; if (host.includes('localhost') || host.includes('127.0.0.1')) { // 本地开发环境:使用实际的端口号(可能是3000或3001) const protocol = 'http'; return `${protocol}://${host}/api/auth/google/callback`; } else if (host.includes('movieflow.net')) { return 'https://www.movieflow.net/api/auth/google/callback'; } else if (host.includes('movieflow.ai')) { return 'https://www.movieflow.ai/api/auth/google/callback'; } else { // 默认使用生产环境 return 'https://www.movieflow.ai/api/auth/google/callback'; } } /** * 处理GET请求 - Google OAuth回调 * 将GET请求重定向到页面路由进行处理 */ export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); const code = searchParams.get('code'); const state = searchParams.get('state'); if (!code || !state) { return NextResponse.json( { success: false, message: 'Missing required parameters: code and state' }, { status: 400 } ); } // 重定向到页面路由,让页面处理OAuth回调 const callbackUrl = `/users/oauth/callback?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}`; // 修复:确保使用正确的域名进行重定向 const host = request.headers.get('host') || 'www.movieflow.net'; const protocol = request.headers.get('x-forwarded-proto') || 'https'; const fullCallbackUrl = `${protocol}://${host}${callbackUrl}`; console.log('🔍 前端API重定向调试:'); console.log(' - request.url:', request.url); console.log(' - host header:', host); console.log(' - protocol:', protocol); console.log(' - 重定向到:', fullCallbackUrl); return NextResponse.redirect(fullCallbackUrl); }