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调用smartvideo-server的register_with_invite接口 const smartvideoBaseUrl = process.env.NEXT_PUBLIC_SMARTVIDEO_URL; console.log('开始调用smartvideo-server后端:', smartvideoBaseUrl); // 从Google ID Token中解析用户信息(简单解析,仅用于获取email等基本信息) let googleUserInfo: any = {}; try { // 解码Google ID Token的payload部分(注意:这里只是为了获取基本信息,真正的验证在后端进行) const tokenParts = id_token.split('.'); if (tokenParts.length === 3) { const payload = JSON.parse(atob(tokenParts[1])); googleUserInfo = { email: payload.email, name: payload.name, given_name: payload.given_name, family_name: payload.family_name }; } } catch (e) { console.warn('解析Google ID Token失败,使用空的用户信息:', e); } const backendResponse = await fetch(`${smartvideoBaseUrl}/api/user_fission/register_with_invite`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({ email: googleUserInfo.email, // 从Google Token中获取的邮箱 name: googleUserInfo.name, // 从Google Token中获取的姓名 google_id_token: id_token, // Google ID Token auth_type: 'GOOGLE', // 标识为Google OAuth注册 invite_code: inviteCode || stateData.inviteCode || undefined }) }); console.log('smartvideo-server后端响应状态:', backendResponse.status); const backendResult = await backendResponse.json(); if (!backendResponse.ok || !backendResult.successful) { console.error('smartvideo-server处理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_id, email: backendResult.data?.email }); // 返回成功结果,格式与原有保持一致以确保前端兼容性 return NextResponse.json({ success: true, data: { // 包含token以支持前端认证 token: backendResult.data.token, user: { userId: backendResult.data.user_id, userName: backendResult.data.name, name: backendResult.data.name, email: backendResult.data.email, authType: backendResult.data.auth_type, isNewUser: true // 通过register_with_invite接口的都是新用户或首次OAuth绑定 }, message: 'Google OAuth registration successful with credit rewards' } }); } 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); }