"use client"; import React, { useEffect, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { CheckCircle, XCircle, Loader2, AlertTriangle } from "lucide-react"; import type { OAuthCallbackParams } from "@/app/types/google-oauth"; import { baseUrl } from '@/lib/env'; // 根据后端实际返回格式定义响应类型 interface GoogleOAuthResponse { code: number; message: string; data: { token: string; user: { userId: string; userName: string; name: string; email: string; authType: "GOOGLE"; avatar: string; isNewUser: boolean; }; message: string; }; successful: boolean; } export default function OAuthCallback() { const router = useRouter(); const searchParams = useSearchParams(); const [status, setStatus] = useState<"loading" | "success" | "error" | "conflict">("loading"); const [message, setMessage] = useState(""); const [conflictData, setConflictData] = useState(null); useEffect(() => { const handleOAuthCallback = async () => { try { console.log('🎯 Google OAuth 回调页面开始处理...'); console.log('📍 回调页面调试信息:'); console.log(' - 完整回调 URL:', window.location.href); console.log(' - 回调域名:', window.location.hostname); console.log(' - 回调协议:', window.location.protocol); console.log(' - 回调路径:', window.location.pathname); console.log(' - URL 查询参数:', window.location.search); // 获取URL参数 const params: OAuthCallbackParams = { code: searchParams.get("code") || undefined, state: searchParams.get("state") || undefined, error: searchParams.get("error") || undefined, error_description: searchParams.get("error_description") || undefined, }; console.log('📦 获取到的URL参数:', params); // 检查是否有错误 if (params.error) { console.error('OAuth错误:', params.error, params.error_description); setStatus("error"); setMessage(params.error_description || `OAuth error: ${params.error}`); return; } // 验证必需参数 if (!params.code || !params.state) { console.error('缺少必需的OAuth参数:', { code: !!params.code, state: !!params.state }); setStatus("error"); setMessage("Missing required OAuth parameters"); return; } // 解析state参数获取邀请码等信息 let stateData: any = {}; try { stateData = JSON.parse(params.state); console.log('解析后的State数据:', stateData); } catch (e) { console.warn('无法解析state参数:', params.state, e); } // 从 sessionStorage 获取邀请码 let inviteCode: string | undefined = undefined; try { const sessionInviteCode = sessionStorage.getItem("inviteCode"); if (sessionInviteCode) { inviteCode = sessionInviteCode; console.log('从 sessionStorage 获取到邀请码:', inviteCode); } } catch (e) { console.warn('无法从 sessionStorage 获取邀请码:', e); } // 优先级:sessionStorage > state参数 > undefined const finalInviteCode = inviteCode || stateData.inviteCode || undefined; console.log('开始处理Google OAuth回调, code:', params.code?.substring(0, 20) + '...'); console.log('State数据:', stateData); console.log('最终使用的邀请码:', finalInviteCode); // 根据 jiekou.md 文档调用统一的 Python OAuth 接口 // 使用统一配置中的 baseUrl console.log('🔧 调用 Python OAuth 接口:', baseUrl); const response = await fetch(`${baseUrl}/api/oauth/google`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify({ code: params.code, state: params.state, invite_code: finalInviteCode || null }) }); console.log('Python OAuth接口响应状态:', response.status); const result: GoogleOAuthResponse = await response.json(); console.log('🔍 后端返回的完整数据:', result); // 根据后端实际返回格式判断成功状态 // 后端返回: { code: 0, successful: true } 表示成功 const isSuccess = response.ok && result.successful && result.code === 0; if (!isSuccess) { console.error('Python OAuth接口处理失败:', result); // 处理常见错误码 if (result.message?.includes('GOOGLE_TOKEN_EXCHANGE_FAILED')) { throw new Error('Google authorization failed. Please try again.'); } else if (result.message?.includes('INVALID_ID_TOKEN')) { throw new Error('Invalid Google token. Please try again.'); } else if (result.message?.includes('UPSTREAM_AUTH_ERROR')) { throw new Error('Authentication service error. Please try again later.'); } // 根据后端返回格式处理错误 const errorMessage = result.message || `OAuth failed (code: ${result.code})`; throw new Error(errorMessage); } console.log('🎉 Google OAuth成功:', { userId: result.data?.user?.userId, email: result.data?.user?.email, isNewUser: result.data?.user?.isNewUser, code: result.code, successful: result.successful }); // 处理成功结果 console.log('✅ Google登录成功,完整响应:', result); setStatus("success"); setMessage(result.data.message || "Login successful! Redirecting to dashboard..."); // 根据后端实际响应格式保存用户信息 const { token, user } = result.data; // 保存用户信息到localStorage const userData = { userId: user.userId, userName: user.userName, name: user.name, email: user.email, authType: user.authType, avatar: user.avatar, isNewUser: user.isNewUser }; console.log('💾 保存用户数据到 localStorage:', userData); localStorage.setItem('currentUser', JSON.stringify(userData)); if (token) { console.log('🔑 保存 token 到 localStorage'); localStorage.setItem('token', token); } console.log('⏰ 准备在2秒后跳转到 /movies'); // 2秒后跳转到主页 setTimeout(() => { const returnUrl = '/movies'; console.log('🚀 开始跳转到:', returnUrl); window.location.href = returnUrl; }, 2000); } catch (error: any) { console.error("OAuth callback error:", error); // 检查是否是网络连接错误 if (error.code === 'ECONNREFUSED' || error.message?.includes('fetch failed')) { console.error('网络连接失败,可能是后端服务不可用'); setStatus("error"); setMessage('Backend service unavailable. Please try again later.'); return; } // 检查是否是 JSON 解析错误 if (error.message?.includes('JSON') || error.name === 'SyntaxError') { console.error('响应数据解析失败:', error); setStatus("error"); setMessage('Invalid response format from backend services'); return; } // 处理邮箱冲突错误 if (error.type === 'EMAIL_CONFLICT') { setStatus("conflict"); setMessage(error.message); setConflictData(error.data); } else { setStatus("error"); setMessage(error.message || "OAuth callback processing failed"); } } }; handleOAuthCallback(); }, [searchParams, router]); const handleBindAccount = async () => { try { // 这里应该实现账户绑定逻辑 // 需要调用 /api/auth/google/bind 接口 console.log("Account binding not yet implemented"); setMessage("Account binding feature is not yet implemented"); } catch (error: any) { console.error("Account binding failed:", error); setMessage(error.message || "Account binding failed"); } }; const handleReturnToLogin = () => { router.push("/login"); }; const renderContent = () => { switch (status) { case "loading": return (

Processing OAuth callback...

); case "success": return (

Login Successful

{message}

); case "conflict": return (

Account Conflict

{message}

{conflictData && (

Email: {conflictData.existingUser?.email}

)}
); case "error": return (

OAuth Failed

{message}

); } }; return (
{status === "loading" && (

OAuth Callback

Please wait while we process your authentication

)} {renderContent()}
); }