// src/contexts/AuthContext.js - Session版本 import React, { createContext, useContext, useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useToast } from '@chakra-ui/react'; // API基础URL配置 const isProduction = process.env.NODE_ENV === 'production'; const API_BASE_URL = isProduction ? "" : process.env.REACT_APP_API_URL || "http://49.232.185.254:5000"; // 创建认证上下文 const AuthContext = createContext(); // 自定义Hook export const useAuth = () => { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within an AuthProvider'); } return context; }; // 认证提供者组件 export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isAuthenticated, setIsAuthenticated] = useState(false); const navigate = useNavigate(); const toast = useToast(); // 检查Session状态 const checkSession = async () => { try { console.log('🔍 检查Session状态...'); const response = await fetch(`${API_BASE_URL}/api/auth/session`, { method: 'GET', credentials: 'include', // 重要:包含cookie headers: { 'Content-Type': 'application/json', } }); if (!response.ok) { throw new Error('Session检查失败'); } const data = await response.json(); console.log('📦 Session数据:', data); if (data.isAuthenticated && data.user) { setUser(data.user); setIsAuthenticated(true); } else { setUser(null); setIsAuthenticated(false); } } catch (error) { console.error('❌ Session检查错误:', error); setUser(null); setIsAuthenticated(false); } finally { setIsLoading(false); } }; // 初始化时检查Session useEffect(() => { checkSession(); }, []); // 监听路由变化,检查session(处理微信登录回调) useEffect(() => { const handleRouteChange = () => { // 如果是从微信回调返回的,重新检查session if (window.location.pathname === '/home' && !isAuthenticated) { checkSession(); } }; window.addEventListener('popstate', handleRouteChange); return () => window.removeEventListener('popstate', handleRouteChange); }, [isAuthenticated]); // 更新本地用户的便捷方法 const updateUser = (partial) => { setUser((prev) => ({ ...(prev || {}), ...partial })); }; // 传统登录方法 const login = async (credential, password, loginType = 'email') => { try { setIsLoading(true); console.log('🔐 开始登录流程:', { credential, loginType }); const formData = new URLSearchParams(); formData.append('password', password); if (loginType === 'username') { formData.append('username', credential); } else if (loginType === 'email') { formData.append('email', credential); } else if (loginType === 'phone') { formData.append('username', credential); } console.log('📤 发送登录请求到:', `${API_BASE_URL}/api/auth/login`); console.log('📝 请求数据:', { credential, loginType, formData: formData.toString() }); const response = await fetch(`${API_BASE_URL}/api/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, credentials: 'include', // 包含cookie body: formData }); console.log('📨 响应状态:', response.status, response.statusText); console.log('📨 响应头:', Object.fromEntries(response.headers.entries())); // 获取响应文本,然后尝试解析JSON const responseText = await response.text(); console.log('📨 响应原始内容:', responseText); let data; try { data = JSON.parse(responseText); console.log('✅ JSON解析成功:', data); } catch (parseError) { console.error('❌ JSON解析失败:', parseError); console.error('📄 响应内容:', responseText); throw new Error(`服务器响应格式错误: ${responseText.substring(0, 100)}...`); } if (!response.ok || !data.success) { throw new Error(data.error || '登录失败'); } // 更新状态 setUser(data.user); setIsAuthenticated(true); toast({ title: "登录成功", description: "欢迎回来!", status: "success", duration: 3000, isClosable: true, }); return { success: true }; } catch (error) { console.error('❌ 登录错误:', error); toast({ title: "登录失败", description: error.message || "请检查您的登录信息", status: "error", duration: 3000, isClosable: true, }); return { success: false, error: error.message }; } finally { setIsLoading(false); } }; // 注册方法 const register = async (username, email, password) => { try { setIsLoading(true); const formData = new URLSearchParams(); formData.append('username', username); formData.append('email', email); formData.append('password', password); const response = await fetch(`${API_BASE_URL}/api/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, credentials: 'include', body: formData }); const data = await response.json(); if (!response.ok || !data.success) { throw new Error(data.error || '注册失败'); } // 注册成功后自动登录 setUser(data.user); setIsAuthenticated(true); toast({ title: "注册成功", description: "欢迎加入价值前沿!", status: "success", duration: 3000, isClosable: true, }); return { success: true }; } catch (error) { console.error('注册错误:', error); toast({ title: "注册失败", description: error.message || "注册失败,请稍后重试", status: "error", duration: 3000, isClosable: true, }); return { success: false, error: error.message }; } finally { setIsLoading(false); } }; // 手机号注册 const registerWithPhone = async (phone, code, username, password) => { try { setIsLoading(true); const response = await fetch(`${API_BASE_URL}/api/auth/register/phone`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify({ phone, code, username, password }) }); const data = await response.json(); if (!response.ok || !data.success) { throw new Error(data.error || '注册失败'); } // 注册成功后自动登录 setUser(data.user); setIsAuthenticated(true); toast({ title: "注册成功", description: "欢迎加入价值前沿!", status: "success", duration: 3000, isClosable: true, }); return { success: true }; } catch (error) { console.error('手机注册错误:', error); toast({ title: "注册失败", description: error.message || "注册失败,请稍后重试", status: "error", duration: 3000, isClosable: true, }); return { success: false, error: error.message }; } finally { setIsLoading(false); } }; // 邮箱注册 const registerWithEmail = async (email, code, username, password) => { try { setIsLoading(true); const response = await fetch(`${API_BASE_URL}/api/auth/register/email`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify({ email, code, username, password }) }); const data = await response.json(); if (!response.ok || !data.success) { throw new Error(data.error || '注册失败'); } // 注册成功后自动登录 setUser(data.user); setIsAuthenticated(true); toast({ title: "注册成功", description: "欢迎加入价值前沿!", status: "success", duration: 3000, isClosable: true, }); return { success: true }; } catch (error) { console.error('邮箱注册错误:', error); toast({ title: "注册失败", description: error.message || "注册失败,请稍后重试", status: "error", duration: 3000, isClosable: true, }); return { success: false, error: error.message }; } finally { setIsLoading(false); } }; // 发送手机验证码 const sendSmsCode = async (phone) => { try { const response = await fetch(`${API_BASE_URL}/api/auth/send-sms-code`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ phone }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || '发送失败'); } toast({ title: "验证码已发送", description: "请查收短信", status: "success", duration: 3000, isClosable: true, }); return { success: true }; } catch (error) { console.error('SMS code error:', error); toast({ title: "发送失败", description: error.message || "请稍后重试", status: "error", duration: 3000, isClosable: true, }); return { success: false, error: error.message }; } }; // 发送邮箱验证码 const sendEmailCode = async (email) => { try { const response = await fetch(`${API_BASE_URL}/api/auth/send-email-code`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || '发送失败'); } toast({ title: "验证码已发送", description: "请查收邮件", status: "success", duration: 3000, isClosable: true, }); return { success: true }; } catch (error) { console.error('Email code error:', error); toast({ title: "发送失败", description: error.message || "请稍后重试", status: "error", duration: 3000, isClosable: true, }); return { success: false, error: error.message }; } }; // 登出方法 const logout = async () => { try { // 调用后端登出API await fetch(`${API_BASE_URL}/api/auth/logout`, { method: 'POST', credentials: 'include' }); // 清除本地状态 setUser(null); setIsAuthenticated(false); toast({ title: "已登出", description: "您已成功退出登录", status: "info", duration: 2000, isClosable: true, }); // 跳转到登录页面 navigate('/auth/signin'); } catch (error) { console.error('Logout error:', error); // 即使API调用失败也清除本地状态 setUser(null); setIsAuthenticated(false); navigate('/auth/signin'); } }; // 检查用户是否有特定权限 const hasRole = (role) => { return user && user.role === role; }; // 刷新session(可选) const refreshSession = async () => { await checkSession(); }; // 提供给子组件的值 const value = { user, isAuthenticated, isLoading, updateUser, login, register, registerWithPhone, registerWithEmail, sendSmsCode, sendEmailCode, logout, hasRole, refreshSession, checkSession }; return ( {children} ); };