diff --git a/app.py b/app.py
index 6619ba39..667c82c7 100755
--- a/app.py
+++ b/app.py
@@ -374,13 +374,16 @@ else:
# Cookie 配置 - 重要:HTTPS 环境必须设置 SECURE=True
app.config['SESSION_COOKIE_SECURE'] = True # 生产环境使用 HTTPS,必须为 True
app.config['SESSION_COOKIE_HTTPONLY'] = True # 生产环境应设为True,防止XSS攻击
-app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # 使用'Lax'以平衡安全性和功能性
+# SameSite='None' 允许微信内置浏览器在 OAuth 重定向后携带 Cookie
+# 必须配合 Secure=True 使用(已在上面配置)
+app.config['SESSION_COOKIE_SAMESITE'] = 'None' # 微信浏览器兼容性:必须为 None
app.config['SESSION_COOKIE_DOMAIN'] = None # 不限制域名
app.config['SESSION_COOKIE_PATH'] = '/' # 设置cookie路径
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # session持续7天
app.config['REMEMBER_COOKIE_DURATION'] = timedelta(days=30) # 记住登录30天
app.config['REMEMBER_COOKIE_SECURE'] = True # 生产环境使用 HTTPS,必须为 True
app.config['REMEMBER_COOKIE_HTTPONLY'] = True # 防止XSS攻击
+app.config['REMEMBER_COOKIE_SAMESITE'] = 'None' # 微信浏览器兼容性
# 初始化 Flask-Session(仅在启用 Redis Session 时)
if USE_REDIS_SESSION:
@@ -3447,7 +3450,16 @@ def register_with_phone():
@app.route('/api/account/phone/send-code', methods=['POST'])
def send_sms_bind_code():
"""发送绑定手机验证码(需已登录)"""
+ # 调试日志:检查 session 状态
+ user_agent = request.headers.get('User-Agent', '')
+ is_wechat = 'MicroMessenger' in user_agent
+ print(f"[绑定手机验证码] User-Agent: {user_agent[:100]}...")
+ print(f"[绑定手机验证码] 是否微信浏览器: {is_wechat}")
+ print(f"[绑定手机验证码] session 内容: logged_in={session.get('logged_in')}, user_id={session.get('user_id')}")
+ print(f"[绑定手机验证码] Cookie: {request.cookies.get('session', 'None')[:20] if request.cookies.get('session') else 'None'}...")
+
if not session.get('logged_in'):
+ print(f"[绑定手机验证码] ❌ 未登录,拒绝请求")
return jsonify({'error': '未登录'}), 401
data = request.get_json()
diff --git a/src/components/Navbars/components/MobileDrawer/MobileDrawer.js b/src/components/Navbars/components/MobileDrawer/MobileDrawer.js
index f7091906..bc1218ed 100644
--- a/src/components/Navbars/components/MobileDrawer/MobileDrawer.js
+++ b/src/components/Navbars/components/MobileDrawer/MobileDrawer.js
@@ -92,7 +92,9 @@ const MobileDrawer = memo(({
/>
{getDisplayName()}
- {user.email}
+ {user.phone && (
+ {user.phone}
+ )}
diff --git a/src/components/Navbars/components/Navigation/PersonalCenterMenu.js b/src/components/Navbars/components/Navigation/PersonalCenterMenu.js
index d68a1611..510f8281 100644
--- a/src/components/Navbars/components/Navigation/PersonalCenterMenu.js
+++ b/src/components/Navbars/components/Navigation/PersonalCenterMenu.js
@@ -61,7 +61,6 @@ const PersonalCenterMenu = memo(({ user, handleLogout }) => {
{/* 用户信息区 */}
{getDisplayName()}
- {user.email}
{user.phone && (
{user.phone}
)}
diff --git a/src/components/Navbars/components/UserMenu/TabletUserMenu.js b/src/components/Navbars/components/UserMenu/TabletUserMenu.js
index 5ca9a8b3..dc813313 100644
--- a/src/components/Navbars/components/UserMenu/TabletUserMenu.js
+++ b/src/components/Navbars/components/UserMenu/TabletUserMenu.js
@@ -75,7 +75,6 @@ const TabletUserMenu = memo(({
{/* 用户信息区 */}
{getDisplayName()}
- {user.email}
{user.phone && (
{user.phone}
)}
diff --git a/src/views/Settings/SettingsPage.js b/src/views/Settings/SettingsPage.js
index a869ccc6..b5aa4c5d 100644
--- a/src/views/Settings/SettingsPage.js
+++ b/src/views/Settings/SettingsPage.js
@@ -11,16 +11,10 @@ import {
Input,
FormControl,
FormLabel,
- Switch,
Card,
CardBody,
CardHeader,
- Divider,
useToast,
- Alert,
- AlertIcon,
- AlertTitle,
- AlertDescription,
Modal,
ModalOverlay,
ModalContent,
@@ -30,31 +24,17 @@ import {
ModalCloseButton,
useDisclosure,
Badge,
- IconButton,
- Textarea,
- Select,
- useColorMode,
- useColorModeValue,
Tabs,
TabList,
TabPanels,
Tab,
TabPanel,
- InputGroup,
- InputRightElement,
PinInput,
- PinInputField,
- SimpleGrid
+ PinInputField
} from '@chakra-ui/react';
import {
- EditIcon,
- ViewIcon,
- ViewOffIcon,
LinkIcon,
- DeleteIcon, // 替换 UnlinkIcon
- WarningIcon,
- CheckIcon,
- CloseIcon
+ DeleteIcon
} from '@chakra-ui/icons';
import { FaWeixin, FaMobile, FaEnvelope } from 'react-icons/fa';
import { useAuth } from '../../contexts/AuthContext';
@@ -63,8 +43,7 @@ import { logger } from '../../utils/logger';
import { useProfileEvents } from '../../hooks/useProfileEvents';
export default function SettingsPage() {
- const { user, updateUser, logout } = useAuth();
- const { colorMode, toggleColorMode } = useColorMode();
+ const { user, updateUser } = useAuth();
const toast = useToast();
// 深色模式固定颜色(Settings 页面始终使用深色主题)
@@ -78,27 +57,11 @@ export default function SettingsPage() {
const profileEvents = useProfileEvents({ pageType: 'settings' });
// 模态框状态
- const { isOpen: isPasswordOpen, onOpen: onPasswordOpen, onClose: onPasswordClose } = useDisclosure();
const { isOpen: isPhoneOpen, onOpen: onPhoneOpen, onClose: onPhoneClose } = useDisclosure();
const { isOpen: isEmailOpen, onOpen: onEmailOpen, onClose: onEmailClose } = useDisclosure();
- const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure();
// 表单状态
const [isLoading, setIsLoading] = useState(false);
- const [showPassword, setShowPassword] = useState(false);
- const [passwordForm, setPasswordForm] = useState({
- currentPassword: '',
- newPassword: '',
- confirmPassword: ''
- });
-
- // 密码状态 - 默认假设是普通用户,获取到数据后再更新
- const [passwordStatus, setPasswordStatus] = useState({
- isWechatUser: false,
- hasPassword: true,
- needsFirstTimeSetup: false
- });
- const [passwordStatusLoading, setPasswordStatusLoading] = useState(true);
const [phoneForm, setPhoneForm] = useState({
phone: '',
verificationCode: ''
@@ -107,162 +70,6 @@ export default function SettingsPage() {
email: '',
verificationCode: ''
});
- const [blockedKeywords, setBlockedKeywords] = useState(user?.blocked_keywords || '');
-
- // 通知设置状态
- const [notifications, setNotifications] = useState({
- email_notifications: user?.email_notifications ?? true,
- sms_notifications: user?.sms_notifications ?? false,
- wechat_notifications: user?.wechat_notifications ?? false,
- system_updates: true,
- investment_alerts: true,
- community_activities: true,
- marketing_emails: false
- });
-
- // 隐私设置状态
- const [privacy, setPrivacy] = useState({
- privacy_level: user?.privacy_level || 'public',
- show_investment_data: true,
- show_trading_history: false,
- allow_friend_requests: true
- });
-
- // 获取密码状态
- const fetchPasswordStatus = async () => {
- try {
- const API_BASE_URL = getApiBase();
-
- logger.api.request('GET', '/api/account/password-status', null);
-
- const response = await fetch(`${API_BASE_URL}/api/account/password-status`, {
- method: 'GET',
- credentials: 'include'
- });
-
- if (response.ok) {
- const data = await response.json();
- logger.api.response('GET', '/api/account/password-status', response.status, data);
-
- if (data.success) {
- logger.debug('SettingsPage', '密码状态获取成功', data.data);
- setPasswordStatus(data.data);
- }
- }
- } catch (error) {
- logger.error('SettingsPage', 'fetchPasswordStatus', error);
- } finally {
- setPasswordStatusLoading(false);
- }
- };
-
- // 组件加载时获取密码状态
- React.useEffect(() => {
- fetchPasswordStatus();
- }, []);
-
- // 修改密码
- const handlePasswordChange = async () => {
- const isFirstTimeSetup = passwordStatus.needsFirstTimeSetup;
-
- // 如果不是首次设置且未提供当前密码
- if (!isFirstTimeSetup && !passwordForm.currentPassword) {
- toast({
- title: "请输入当前密码",
- description: "修改密码需要验证当前密码",
- status: "error",
- duration: 3000,
- isClosable: true,
- });
- return;
- }
-
- if (passwordForm.newPassword !== passwordForm.confirmPassword) {
- toast({
- title: "密码不匹配",
- description: "新密码与确认密码不一致",
- status: "error",
- duration: 3000,
- isClosable: true,
- });
- return;
- }
-
- if (passwordForm.newPassword.length < 6) {
- toast({
- title: "密码太短",
- description: "密码至少需要6个字符",
- status: "error",
- duration: 3000,
- isClosable: true,
- });
- return;
- }
-
- setIsLoading(true);
- try {
- // 调用后端API修改密码
- const API_BASE_URL = getApiBase();
-
- const response = await fetch(`${API_BASE_URL}/api/account/change-password`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- credentials: 'include', // 包含认证信息
- body: JSON.stringify({
- currentPassword: passwordForm.currentPassword,
- newPassword: passwordForm.newPassword,
- isFirstSet: passwordStatus.needsFirstTimeSetup
- })
- });
-
- const data = await response.json();
-
- if (response.ok && data.success) {
- const isFirstSet = passwordStatus.needsFirstTimeSetup;
-
- // 🎯 追踪密码修改成功
- profileEvents.trackPasswordChanged(true);
-
- toast({
- title: isFirstSet ? "密码设置成功" : "密码修改成功",
- description: isFirstSet ? "您现在可以使用手机号+密码登录了" : "请重新登录",
- status: "success",
- duration: 3000,
- isClosable: true,
- });
-
- setPasswordForm({ currentPassword: '', newPassword: '', confirmPassword: '' });
- onPasswordClose();
-
- // 刷新密码状态
- fetchPasswordStatus();
-
- // 如果是修改密码(非首次设置),需要重新登录
- if (!isFirstSet) {
- setTimeout(() => {
- logout();
- }, 1000);
- }
- } else {
- throw new Error(data.error || '密码修改失败');
- }
- } catch (error) {
- // 🎯 追踪密码修改失败
- profileEvents.trackPasswordChanged(false, error.message);
-
- toast({
- title: "修改失败",
- description: error.message,
- status: "error",
- duration: 3000,
- isClosable: true,
- });
- } finally {
- setIsLoading(false);
- }
- };
// 发送验证码
const sendVerificationCode = async (type) => {
@@ -410,101 +217,6 @@ export default function SettingsPage() {
}
};
- // 保存通知设置
- const saveNotificationSettings = async () => {
- setIsLoading(true);
- try {
- logger.debug('SettingsPage', '保存通知设置', notifications);
-
- // 这里应该调用后端API保存设置
- await new Promise(resolve => setTimeout(resolve, 1000));
-
- updateUser(notifications);
-
- // 🎯 追踪通知偏好更改
- profileEvents.trackNotificationPreferencesChanged({
- email: notifications.email_notifications,
- push: notifications.system_updates,
- sms: notifications.sms_notifications
- });
-
- // ❌ 移除设置保存成功toast
- logger.info('SettingsPage', '通知设置已保存');
- } catch (error) {
- logger.error('SettingsPage', 'saveNotificationSettings', error);
-
- toast({
- title: "保存失败",
- description: error.message,
- status: "error",
- duration: 3000,
- isClosable: true,
- });
- } finally {
- setIsLoading(false);
- }
- };
-
- // 保存隐私设置
- const savePrivacySettings = async () => {
- setIsLoading(true);
- try {
- logger.debug('SettingsPage', '保存隐私设置', { privacy, blockedKeywords });
-
- // 这里应该调用后端API保存设置
- await new Promise(resolve => setTimeout(resolve, 1000));
-
- updateUser({
- privacy_level: privacy.privacy_level,
- blocked_keywords: blockedKeywords
- });
-
- // ❌ 移除设置保存成功toast
- logger.info('SettingsPage', '隐私设置已保存');
- } catch (error) {
- logger.error('SettingsPage', 'savePrivacySettings', error);
-
- toast({
- title: "保存失败",
- description: error.message,
- status: "error",
- duration: 3000,
- isClosable: true,
- });
- } finally {
- setIsLoading(false);
- }
- };
-
- // 注销账户
- const handleDeleteAccount = async () => {
- setIsLoading(true);
- try {
- // 这里应该调用后端API注销账户
- await new Promise(resolve => setTimeout(resolve, 2000));
-
- toast({
- title: "账户已注销",
- description: "感谢您的使用",
- status: "info",
- duration: 3000,
- isClosable: true,
- });
-
- logout();
- } catch (error) {
- toast({
- title: "注销失败",
- description: error.message,
- status: "error",
- duration: 3000,
- isClosable: true,
- });
- } finally {
- setIsLoading(false);
- }
- };
-
return (
@@ -513,51 +225,13 @@ export default function SettingsPage() {
- 账户安全
- 通知设置
- 隐私设置
- 界面设置
- 危险操作
+ 账户绑定
- {/* 账户安全 */}
+ {/* 账户绑定 */}
- {/* 密码设置 */}
-
-
- 密码设置
-
-
-
-
-
- {passwordStatus.needsFirstTimeSetup ? '设置登录密码' : '登录密码'}
-
-
- {passwordStatus.needsFirstTimeSetup
- ? '您通过微信登录,建议设置密码以便其他方式登录'
- : '定期更换密码,保护账户安全'
- }
-
- {passwordStatus.isWechatUser && (
-
- 微信用户
-
- )}
-
- }
- onClick={onPasswordOpen}
- isLoading={passwordStatusLoading}
- >
- {passwordStatus.needsFirstTimeSetup ? '设置密码' : '修改密码'}
-
-
-
-
-
{/* 手机号绑定 */}
@@ -731,458 +405,12 @@ export default function SettingsPage() {
- {/* 两步验证 */}
-
-
- 两步验证
-
-
-
-
- 安全验证
-
- 开启两步验证,提高账户安全性
-
-
-
-
-
-
-
-
-
- {/* 通知设置 */}
-
-
-
-
- 通知方式
-
-
-
-
-
- 邮件通知
-
- 接收邮件通知
-
-
- setNotifications(prev => ({
- ...prev,
- email_notifications: e.target.checked
- }))}
- />
-
-
-
-
- 短信通知
-
- 接收短信通知(需绑定手机号)
-
-
- setNotifications(prev => ({
- ...prev,
- sms_notifications: e.target.checked
- }))}
- />
-
-
-
-
- 微信通知
-
- 接收微信通知(需绑定微信)
-
-
- setNotifications(prev => ({
- ...prev,
- wechat_notifications: e.target.checked
- }))}
- />
-
-
-
-
-
-
-
- 通知类型
-
-
-
-
- 系统更新通知
- setNotifications(prev => ({
- ...prev,
- system_updates: e.target.checked
- }))}
- />
-
-
-
- 投资提醒
- setNotifications(prev => ({
- ...prev,
- investment_alerts: e.target.checked
- }))}
- />
-
-
-
- 社区动态
- setNotifications(prev => ({
- ...prev,
- community_activities: e.target.checked
- }))}
- />
-
-
-
- 营销邮件
- setNotifications(prev => ({
- ...prev,
- marketing_emails: e.target.checked
- }))}
- />
-
-
-
-
-
-
-
-
-
-
-
- {/* 隐私设置 */}
-
-
-
-
- 隐私级别
-
-
-
-
- 个人资料可见性
-
-
-
-
-
-
-
- 显示投资数据
- setPrivacy(prev => ({
- ...prev,
- show_investment_data: e.target.checked
- }))}
- />
-
-
-
- 显示交易历史
- setPrivacy(prev => ({
- ...prev,
- show_trading_history: e.target.checked
- }))}
- />
-
-
-
- 允许好友请求
- setPrivacy(prev => ({
- ...prev,
- allow_friend_requests: e.target.checked
- }))}
- />
-
-
-
-
-
-
-
-
- 屏蔽设置
-
-
-
- 屏蔽关键词
-
-
-
-
-
-
-
-
-
-
- {/* 界面设置 */}
-
-
-
-
- 外观设置
-
-
-
-
-
- 深色模式
-
- 切换到深色主题
-
-
-
-
-
-
-
- 语言
-
- 选择界面语言
-
-
-
-
-
-
-
-
-
-
- 数据管理
-
-
-
-
-
- 数据导出
-
- 导出您的个人数据
-
-
-
-
-
-
-
- 清除缓存
-
- 清除本地缓存数据
-
-
-
-
-
-
-
-
-
-
- {/* 危险操作 */}
-
-
-
-
- 危险操作区域
-
- 以下操作不可逆,请谨慎操作
-
-
-
-
-
- 注销账户
-
-
-
-
- 注销账户将永久删除您的所有数据,包括:
-
-
- • 个人资料和设置
- • 投资记录和分析数据
- • 社区发布的内容
- • 关注和粉丝关系
-
-
- 此操作不可恢复,请确认您真的要注销账户。
-
-
- }
- onClick={onDeleteOpen}
- maxW="200px"
- >
- 注销账户
-
-
-
-
- {/* 修改密码模态框 */}
-
-
-
-
- {passwordStatus.needsFirstTimeSetup ? '设置登录密码' : '修改密码'}
-
-
-
-
- {/* 微信用户说明 */}
- {passwordStatus.isWechatUser && passwordStatus.needsFirstTimeSetup && (
-
-
-
- 设置密码以便多种方式登录
-
- 您当前通过微信登录,设置密码后可以使用手机号+密码的方式登录。
-
-
-
- )}
-
- {/* 当前密码 - 仅非首次设置且非加载状态时显示 */}
- {!passwordStatusLoading && !passwordStatus.needsFirstTimeSetup && (
-
- 当前密码
-
- setPasswordForm(prev => ({
- ...prev,
- currentPassword: e.target.value
- }))}
- placeholder="请输入当前密码"
- />
-
- : }
- onClick={() => setShowPassword(!showPassword)}
- />
-
-
-
- )}
-
-
- 新密码
- setPasswordForm(prev => ({
- ...prev,
- newPassword: e.target.value
- }))}
- />
-
-
-
- 确认新密码
- setPasswordForm(prev => ({
- ...prev,
- confirmPassword: e.target.value
- }))}
- />
-
-
-
-
-
-
-
-
-
-
{/* 绑定手机号模态框 */}
@@ -1314,45 +542,6 @@ export default function SettingsPage() {
- {/* 注销账户确认模态框 */}
-
-
-
- 注销账户确认
-
-
-
-
-
-
- 警告!
-
- 此操作将永久删除您的账户和所有数据,且无法恢复。
-
-
-
-
-
- 如果您确定要注销账户,请在下方输入 "确认注销" 来确认此操作:
-
-
-
-
-
-
-
-
-
-
-
);
}
\ No newline at end of file
diff --git a/valuefrontier.conf b/valuefrontier.conf
index 4970b0c6..37257054 100644
--- a/valuefrontier.conf
+++ b/valuefrontier.conf
@@ -146,7 +146,7 @@ server {
# 上交所实时行情 WebSocket
location /ws/sse {
- proxy_pass http://49.232.185.254:8765;
+ proxy_pass http://127.0.0.1:8765; # 本机行情服务
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;