/* Copyright (C) 2025 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { API, showError, showSuccess, showWarning } from '../../helpers'; import { Banner, Button, Card, Checkbox, Divider, Form, Input, Modal, Tag, Typography } from '@douyinfe/semi-ui'; import React, { useEffect, useState } from 'react'; import { QRCodeSVG } from 'qrcode.react'; const { Text, Paragraph } = Typography; const TwoFASetting = () => { const [loading, setLoading] = useState(false); const [status, setStatus] = useState({ enabled: false, locked: false, backup_codes_remaining: 0 }); // 模态框状态 const [setupModalVisible, setSetupModalVisible] = useState(false); const [enableModalVisible, setEnableModalVisible] = useState(false); const [disableModalVisible, setDisableModalVisible] = useState(false); const [backupModalVisible, setBackupModalVisible] = useState(false); // 表单数据 const [setupData, setSetupData] = useState(null); const [verificationCode, setVerificationCode] = useState(''); const [backupCodes, setBackupCodes] = useState([]); const [confirmDisable, setConfirmDisable] = useState(false); // 获取2FA状态 const fetchStatus = async () => { try { const res = await API.get('/api/user/2fa/status'); if (res.data.success) { setStatus(res.data.data); } } catch (error) { showError('获取2FA状态失败'); } }; useEffect(() => { fetchStatus(); }, []); // 初始化2FA设置 const handleSetup2FA = async () => { setLoading(true); try { const res = await API.post('/api/user/2fa/setup'); if (res.data.success) { setSetupData(res.data.data); setSetupModalVisible(true); } else { showError(res.data.message); } } catch (error) { showError('设置2FA失败'); } finally { setLoading(false); } }; // 启用2FA const handleEnable2FA = async () => { if (!verificationCode) { showWarning('请输入验证码'); return; } setLoading(true); try { const res = await API.post('/api/user/2fa/enable', { code: verificationCode }); if (res.data.success) { showSuccess('两步验证启用成功!'); setEnableModalVisible(false); setSetupModalVisible(false); setVerificationCode(''); fetchStatus(); } else { showError(res.data.message); } } catch (error) { showError('启用2FA失败'); } finally { setLoading(false); } }; // 禁用2FA const handleDisable2FA = async () => { if (!verificationCode) { showWarning('请输入验证码或备用码'); return; } if (!confirmDisable) { showWarning('请确认您已了解禁用两步验证的后果'); return; } setLoading(true); try { const res = await API.post('/api/user/2fa/disable', { code: verificationCode }); if (res.data.success) { showSuccess('两步验证已禁用'); setDisableModalVisible(false); setVerificationCode(''); setConfirmDisable(false); fetchStatus(); } else { showError(res.data.message); } } catch (error) { showError('禁用2FA失败'); } finally { setLoading(false); } }; // 重新生成备用码 const handleRegenerateBackupCodes = async () => { if (!verificationCode) { showWarning('请输入验证码'); return; } setLoading(true); try { const res = await API.post('/api/user/2fa/backup_codes', { code: verificationCode }); if (res.data.success) { setBackupCodes(res.data.data.backup_codes); showSuccess('备用码重新生成成功'); setVerificationCode(''); fetchStatus(); } else { showError(res.data.message); } } catch (error) { showError('重新生成备用码失败'); } finally { setLoading(false); } }; const copyBackupCodes = () => { const codesText = backupCodes.join('\n'); navigator.clipboard.writeText(codesText).then(() => { showSuccess('备用码已复制到剪贴板'); }).catch(() => { showError('复制失败,请手动复制'); }); }; return (
两步验证设置
两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。
当前状态: {status.enabled ? ( 已启用 ) : ( 未启用 )} {status.locked && ( 账户已锁定 )}
{status.enabled && (
剩余备用码:{status.backup_codes_remaining || 0} 个
)}
{!status.enabled ? ( ) : (
)}
{/* 2FA设置模态框 */}
设置两步验证
} visible={setupModalVisible} onCancel={() => { setSetupModalVisible(false); setSetupData(null); }} footer={null} width={650} style={{ maxWidth: '90vw' }} > {setupData && (
{/* 步骤 1:扫描二维码 */}
1
扫描二维码
使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:
或手动输入密钥:{setupData.secret}
{/* 步骤 2:保存备用码 */}
2
保存备用码
请将以下备用码保存在安全的地方。如果丢失手机,可以使用这些备用码登录:
{setupData.backup_codes.map((code, index) => (
{code}
))}
{/* 步骤 3:验证设置 */}
3
验证设置
输入认证器应用显示的6位数字验证码:
)} {/* 禁用2FA模态框 */}
禁用两步验证 } visible={disableModalVisible} onCancel={() => { setDisableModalVisible(false); setVerificationCode(''); setConfirmDisable(false); }} footer={null} width={550} >
警告:禁用两步验证将会:
  • 降低您账户的安全性
  • 永久删除您的两步验证设置
  • 永久删除所有备用码(包括未使用的)
  • 需要重新完整设置才能再次启用
此操作不可撤销,请谨慎操作!
} className="rounded-lg" />
setConfirmDisable(e.target.checked)} className="text-sm" > 我已了解禁用两步验证将永久删除所有相关设置和备用码,此操作不可撤销
{/* 重新生成备用码模态框 */}
重新生成备用码 } visible={backupModalVisible} onCancel={() => { setBackupModalVisible(false); setVerificationCode(''); setBackupCodes([]); }} footer={null} width={500} >
{backupCodes.length === 0 ? ( <>
) : ( <>
新的备用码已生成 请将以下备用码保存在安全的地方:
{backupCodes.map((code, index) => (
{code}
))}
)}
); }; export default TwoFASetting;