| 31 | import Loading from '../common/ui/Loading'; |
| 32 | |
| 33 | const OAuth2Callback = (props) => { |
| 34 | const { t } = useTranslation(); |
| 35 | const [searchParams] = useSearchParams(); |
| 36 | const [, userDispatch] = useContext(UserContext); |
| 37 | const navigate = useNavigate(); |
| 38 | |
| 39 | // 防止 React 18 Strict Mode 下重复执行 |
| 40 | const hasExecuted = useRef(false); |
| 41 | |
| 42 | // 最大重试次数 |
| 43 | const MAX_RETRIES = 3; |
| 44 | |
| 45 | const sendCode = async (code, state, retry = 0) => { |
| 46 | try { |
| 47 | const { data: resData } = await API.get( |
| 48 | `/api/oauth/${props.type}?code=${code}&state=${state}`, |
| 49 | ); |
| 50 | |
| 51 | const { success, message, data } = resData; |
| 52 | |
| 53 | if (!success) { |
| 54 | // 业务错误不重试,直接显示错误 |
| 55 | showError(message || t('授权失败')); |
| 56 | return; |
| 57 | } |
| 58 | |
| 59 | if (data?.action === 'bind') { |
| 60 | showSuccess(t('绑定成功!')); |
| 61 | navigate('/console/personal'); |
| 62 | } else { |
| 63 | userDispatch({ type: 'login', payload: data }); |
| 64 | localStorage.setItem('user', JSON.stringify(data)); |
| 65 | setUserData(data); |
| 66 | updateAPI(); |
| 67 | showSuccess(t('登录成功!')); |
| 68 | navigate('/console/token'); |
| 69 | } |
| 70 | } catch (error) { |
| 71 | // 网络错误等可重试 |
| 72 | if (retry < MAX_RETRIES) { |
| 73 | // 递增的退避等待 |
| 74 | await new Promise((resolve) => setTimeout(resolve, (retry + 1) * 2000)); |
| 75 | return sendCode(code, state, retry + 1); |
| 76 | } |
| 77 | |
| 78 | // 重试次数耗尽,提示错误并返回设置页面 |
| 79 | showError(error.message || t('授权失败')); |
| 80 | navigate('/console/personal'); |
| 81 | } |
| 82 | }; |
| 83 | |
| 84 | useEffect(() => { |
| 85 | // 防止 React 18 Strict Mode 下重复执行 |
| 86 | if (hasExecuted.current) { |
| 87 | return; |
| 88 | } |
| 89 | hasExecuted.current = true; |
| 90 | |