diff --git a/src/components/contexts/AuthContext.js b/src/components/contexts/AuthContext.js deleted file mode 100644 index a697984e..00000000 --- a/src/components/contexts/AuthContext.js +++ /dev/null @@ -1,568 +0,0 @@ -// 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'; - -// 创建认证上下文 -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/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/auth/login`); - console.log('📝 请求数据:', { - credential, - loginType, - formData: formData.toString() - }); - - const response = await fetch(`/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/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/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/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/auth/send-sms-code`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'include', // 必须包含以支持跨域 session cookie - 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/auth/send-email-code`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - credentials: 'include', // 必须包含以支持跨域 session cookie - 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/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; - }; - - // 检查用户订阅权限 - const hasSubscriptionPermission = async (featureName) => { - try { - // 如果用户未登录,返回免费权限 - if (!isAuthenticated) { - const freePermissions = { - 'related_stocks': false, - 'related_concepts': false, - 'transmission_chain': false, - 'historical_events': 'limited' - }; - return freePermissions[featureName] || false; - } - - // 获取用户权限信息 - const response = await fetch(`/api/subscription/permissions`, { - method: 'GET', - credentials: 'include', - headers: { - 'Content-Type': 'application/json', - } - }); - - if (!response.ok) { - throw new Error('获取权限信息失败'); - } - - const data = await response.json(); - - if (data.success && data.data) { - return data.data.permissions[featureName] || false; - } - - // 如果API调用失败,返回默认权限 - const defaultPermissions = { - 'related_stocks': user?.subscription_type === 'pro' || user?.subscription_type === 'max', - 'related_concepts': user?.subscription_type === 'pro' || user?.subscription_type === 'max', - 'transmission_chain': user?.subscription_type === 'max', - 'historical_events': user?.subscription_type === 'pro' || user?.subscription_type === 'max' ? 'full' : 'limited' - }; - - return defaultPermissions[featureName] || false; - - } catch (error) { - console.error('检查订阅权限失败:', error); - - // 降级处理:基于用户现有的订阅信息 - const fallbackPermissions = { - 'related_stocks': user?.subscription_type === 'pro' || user?.subscription_type === 'max', - 'related_concepts': user?.subscription_type === 'pro' || user?.subscription_type === 'max', - 'transmission_chain': user?.subscription_type === 'max', - 'historical_events': user?.subscription_type === 'pro' || user?.subscription_type === 'max' ? 'full' : 'limited' - }; - - return fallbackPermissions[featureName] || false; - } - }; - - // 获取用户订阅级别 - const getSubscriptionLevel = () => { - if (!isAuthenticated) return 'free'; - return user?.subscription_type || 'free'; - }; - - // 检查是否需要升级订阅 - const requiresUpgrade = (requiredLevel) => { - const currentLevel = getSubscriptionLevel(); - const levels = { 'free': 0, 'pro': 1, 'max': 2 }; - - const currentLevelValue = levels[currentLevel] || 0; - const requiredLevelValue = levels[requiredLevel] || 0; - - return currentLevelValue < requiredLevelValue; - }; - - // 刷新session(可选) - const refreshSession = async () => { - await checkSession(); - }; - - // 提供给子组件的值 - const value = { - user, - isAuthenticated, - isLoading, - updateUser, - login, - register, - registerWithPhone, - registerWithEmail, - sendSmsCode, - sendEmailCode, - logout, - hasRole, - hasSubscriptionPermission, - getSubscriptionLevel, - requiresUpgrade, - refreshSession, - checkSession - }; - - return ( - - {children} - - ); -}; \ No newline at end of file