"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"; // 根据接口文档定义响应类型 interface GoogleOAuthResponse { success: boolean; data: { token: string; user: { userId: string; userName: string; name: string; email: string; authType: "GOOGLE"; avatar: string; isNewUser: boolean; }; message: string; }; message?: string; } 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 接口 // 使用 NEXT_PUBLIC_BASE_URL 配置,默认为 https://77.smartvideo.py.qikongjian.com const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://77.smartvideo.py.qikongjian.com'; 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(); if (!response.ok || !result.success) { 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.'); } throw new Error(result.message || 'Google OAuth failed'); } console.log('Google OAuth成功:', { userId: result.data?.user?.userId, email: result.data?.user?.email, isNewUser: result.data?.user?.isNewUser }); // 处理成功结果 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 }; localStorage.setItem('currentUser', JSON.stringify(userData)); if (token) { localStorage.setItem('token', token); } // 2秒后跳转到主页 setTimeout(() => { const returnUrl = '/movies'; 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()}
); }