2025-10-30 11:14:43 +08:00
|
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
|
|
import { useNavigate } from 'react-router-dom';
|
2025-11-11 19:50:12 +08:00
|
|
|
|
import { Spin, Result, Button, Modal, Input, message } from 'antd';
|
2025-10-30 11:14:43 +08:00
|
|
|
|
import { authApi } from '../services/api';
|
2025-11-06 12:36:32 +08:00
|
|
|
|
import AnnouncementModal from '../components/AnnouncementModal';
|
2025-10-30 11:14:43 +08:00
|
|
|
|
|
|
|
|
|
|
export default function AuthCallback() {
|
|
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
|
const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading');
|
|
|
|
|
|
const [errorMessage, setErrorMessage] = useState('');
|
2025-11-06 12:36:32 +08:00
|
|
|
|
const [showAnnouncement, setShowAnnouncement] = useState(false);
|
2025-11-11 19:50:12 +08:00
|
|
|
|
const [showPasswordModal, setShowPasswordModal] = useState(false);
|
|
|
|
|
|
const [passwordStatus, setPasswordStatus] = useState<any>(null);
|
|
|
|
|
|
const [newPassword, setNewPassword] = useState('');
|
|
|
|
|
|
const [confirmPassword, setConfirmPassword] = useState('');
|
|
|
|
|
|
const [settingPassword, setSettingPassword] = useState(false);
|
2025-10-30 11:14:43 +08:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
const handleCallback = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 后端会通过 Cookie 自动设置认证信息
|
|
|
|
|
|
// 这里只需要验证登录状态
|
2025-12-30 10:03:20 +08:00
|
|
|
|
const currentUser = await authApi.getCurrentUser();
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-12-30 10:03:20 +08:00
|
|
|
|
// 检查是否是首次登录(通过 Cookie 标记)
|
|
|
|
|
|
const isFirstLogin = document.cookie.includes('first_login=true');
|
|
|
|
|
|
|
2025-10-30 11:14:43 +08:00
|
|
|
|
setStatus('success');
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-12-30 10:03:20 +08:00
|
|
|
|
if (isFirstLogin) {
|
|
|
|
|
|
// 首次登录:生成默认密码并显示提示
|
|
|
|
|
|
const defaultPassword = `${currentUser.username}@666`;
|
|
|
|
|
|
const pwdStatus = {
|
|
|
|
|
|
has_password: false,
|
|
|
|
|
|
has_custom_password: false,
|
|
|
|
|
|
username: currentUser.username,
|
|
|
|
|
|
default_password: defaultPassword
|
|
|
|
|
|
};
|
|
|
|
|
|
setPasswordStatus(pwdStatus);
|
|
|
|
|
|
|
|
|
|
|
|
// 清除首次登录标记 Cookie
|
|
|
|
|
|
document.cookie = 'first_login=; path=/; max-age=0';
|
|
|
|
|
|
|
|
|
|
|
|
// 显示密码初始化弹窗
|
2025-11-11 19:50:12 +08:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
setShowPasswordModal(true);
|
|
|
|
|
|
}, 1000);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-12-30 10:03:20 +08:00
|
|
|
|
// 非首次登录:正常流程
|
2025-10-30 11:14:43 +08:00
|
|
|
|
// 从 sessionStorage 获取重定向地址
|
|
|
|
|
|
const redirect = sessionStorage.getItem('login_redirect') || '/';
|
|
|
|
|
|
sessionStorage.removeItem('login_redirect');
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-27 17:29:23 +08:00
|
|
|
|
// 检查是否永久隐藏公告或今日已隐藏
|
|
|
|
|
|
const hideForever = localStorage.getItem('announcement_hide_forever');
|
|
|
|
|
|
const hideToday = localStorage.getItem('announcement_hide_today');
|
|
|
|
|
|
const today = new Date().toDateString();
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-27 17:29:23 +08:00
|
|
|
|
if (hideForever === 'true' || hideToday === today) {
|
|
|
|
|
|
// 延迟一下再跳转,让用户看到成功提示
|
2025-11-06 12:36:32 +08:00
|
|
|
|
setTimeout(() => {
|
2025-11-27 17:29:23 +08:00
|
|
|
|
navigate(redirect);
|
2025-11-06 12:36:32 +08:00
|
|
|
|
}, 1000);
|
|
|
|
|
|
} else {
|
2025-11-27 17:29:23 +08:00
|
|
|
|
// 延迟一下再显示公告,让用户看到成功提示
|
2025-11-06 12:36:32 +08:00
|
|
|
|
setTimeout(() => {
|
2025-11-27 17:29:23 +08:00
|
|
|
|
setShowAnnouncement(true);
|
2025-11-06 12:36:32 +08:00
|
|
|
|
}, 1000);
|
|
|
|
|
|
}
|
2025-10-30 11:14:43 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('登录失败:', error);
|
|
|
|
|
|
setStatus('error');
|
|
|
|
|
|
setErrorMessage('登录失败,请重试');
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
handleCallback();
|
|
|
|
|
|
}, [navigate]);
|
|
|
|
|
|
|
|
|
|
|
|
if (status === 'loading') {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
minHeight: '100vh',
|
2025-12-15 16:38:41 +08:00
|
|
|
|
background: 'linear-gradient(135deg, #4D8088 0%, #5F9EA8 100%)',
|
2025-10-30 11:14:43 +08:00
|
|
|
|
}}>
|
|
|
|
|
|
<div style={{ textAlign: 'center' }}>
|
|
|
|
|
|
<Spin size="large" />
|
|
|
|
|
|
<div style={{ marginTop: 20, color: 'white', fontSize: 16 }}>
|
|
|
|
|
|
正在处理登录...
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (status === 'error') {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
minHeight: '100vh',
|
2025-12-15 16:38:41 +08:00
|
|
|
|
background: 'linear-gradient(135deg, #4D8088 0%, #5F9EA8 100%)',
|
2025-10-30 11:14:43 +08:00
|
|
|
|
}}>
|
|
|
|
|
|
<Result
|
|
|
|
|
|
status="error"
|
|
|
|
|
|
title="登录失败"
|
|
|
|
|
|
subTitle={errorMessage}
|
|
|
|
|
|
extra={
|
|
|
|
|
|
<Button type="primary" onClick={() => navigate('/login')}>
|
|
|
|
|
|
返回登录
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
}
|
|
|
|
|
|
style={{ background: 'white', padding: 40, borderRadius: 8 }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-06 12:36:32 +08:00
|
|
|
|
const handleAnnouncementClose = () => {
|
|
|
|
|
|
setShowAnnouncement(false);
|
|
|
|
|
|
const redirect = sessionStorage.getItem('login_redirect') || '/';
|
|
|
|
|
|
sessionStorage.removeItem('login_redirect');
|
|
|
|
|
|
navigate(redirect);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleDoNotShowToday = () => {
|
2025-11-27 17:29:23 +08:00
|
|
|
|
// 设置今日不再显示
|
|
|
|
|
|
const today = new Date().toDateString();
|
|
|
|
|
|
localStorage.setItem('announcement_hide_today', today);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleNeverShow = () => {
|
|
|
|
|
|
// 设置永久不再显示
|
|
|
|
|
|
localStorage.setItem('announcement_hide_forever', 'true');
|
2025-11-06 12:36:32 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-11 19:50:12 +08:00
|
|
|
|
const handleSetPassword = async () => {
|
2025-12-30 10:03:20 +08:00
|
|
|
|
// 如果没有输入新密码,使用默认密码
|
|
|
|
|
|
const passwordToSet = newPassword || passwordStatus?.default_password;
|
|
|
|
|
|
|
|
|
|
|
|
if (!passwordToSet) {
|
2025-11-11 19:50:12 +08:00
|
|
|
|
message.error('请输入新密码');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-12-30 10:03:20 +08:00
|
|
|
|
if (passwordToSet.length < 6) {
|
2025-11-11 19:50:12 +08:00
|
|
|
|
message.error('密码长度至少为6个字符');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-12-30 10:03:20 +08:00
|
|
|
|
if (newPassword && newPassword !== confirmPassword) {
|
2025-11-11 19:50:12 +08:00
|
|
|
|
message.error('两次输入的密码不一致');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setSettingPassword(true);
|
|
|
|
|
|
try {
|
2025-12-30 10:03:20 +08:00
|
|
|
|
// 首次登录使用初始化接口,后续使用修改接口
|
|
|
|
|
|
const isFirstLogin = !passwordStatus?.has_password;
|
|
|
|
|
|
if (isFirstLogin) {
|
|
|
|
|
|
await authApi.initializePassword(passwordToSet);
|
|
|
|
|
|
message.success('密码初始化成功');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await authApi.setPassword(passwordToSet);
|
|
|
|
|
|
message.success('密码设置成功');
|
|
|
|
|
|
}
|
2025-11-11 19:50:12 +08:00
|
|
|
|
setShowPasswordModal(false);
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-11 19:50:12 +08:00
|
|
|
|
// 继续后续流程
|
|
|
|
|
|
const redirect = sessionStorage.getItem('login_redirect') || '/';
|
|
|
|
|
|
sessionStorage.removeItem('login_redirect');
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-27 17:29:23 +08:00
|
|
|
|
const hideForever = localStorage.getItem('announcement_hide_forever');
|
|
|
|
|
|
const hideToday = localStorage.getItem('announcement_hide_today');
|
|
|
|
|
|
const today = new Date().toDateString();
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-27 17:29:23 +08:00
|
|
|
|
if (hideForever === 'true' || hideToday === today) {
|
2025-11-11 19:50:12 +08:00
|
|
|
|
setTimeout(() => {
|
2025-11-27 17:29:23 +08:00
|
|
|
|
navigate(redirect);
|
2025-11-11 19:50:12 +08:00
|
|
|
|
}, 500);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setTimeout(() => {
|
2025-11-27 17:29:23 +08:00
|
|
|
|
setShowAnnouncement(true);
|
2025-11-11 19:50:12 +08:00
|
|
|
|
}, 500);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
message.error('密码设置失败,请重试');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setSettingPassword(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-12-30 10:03:20 +08:00
|
|
|
|
const handleSkipPasswordSetting = async () => {
|
|
|
|
|
|
// 首次登录时,如果跳过设置,使用默认密码初始化
|
|
|
|
|
|
const isFirstLogin = !passwordStatus?.has_password;
|
|
|
|
|
|
if (isFirstLogin && passwordStatus?.default_password) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await authApi.initializePassword(passwordStatus.default_password);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('初始化默认密码失败:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-11 19:50:12 +08:00
|
|
|
|
setShowPasswordModal(false);
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-11 19:50:12 +08:00
|
|
|
|
// 继续后续流程
|
|
|
|
|
|
const redirect = sessionStorage.getItem('login_redirect') || '/';
|
|
|
|
|
|
sessionStorage.removeItem('login_redirect');
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-27 17:29:23 +08:00
|
|
|
|
const hideForever = localStorage.getItem('announcement_hide_forever');
|
|
|
|
|
|
const hideToday = localStorage.getItem('announcement_hide_today');
|
|
|
|
|
|
const today = new Date().toDateString();
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-27 17:29:23 +08:00
|
|
|
|
if (hideForever === 'true' || hideToday === today) {
|
2025-11-11 19:50:12 +08:00
|
|
|
|
setTimeout(() => {
|
2025-11-27 17:29:23 +08:00
|
|
|
|
navigate(redirect);
|
2025-11-11 19:50:12 +08:00
|
|
|
|
}, 500);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setTimeout(() => {
|
2025-11-27 17:29:23 +08:00
|
|
|
|
setShowAnnouncement(true);
|
2025-11-11 19:50:12 +08:00
|
|
|
|
}, 500);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-10-30 11:14:43 +08:00
|
|
|
|
return (
|
2025-11-06 12:36:32 +08:00
|
|
|
|
<>
|
|
|
|
|
|
<AnnouncementModal
|
|
|
|
|
|
visible={showAnnouncement}
|
|
|
|
|
|
onClose={handleAnnouncementClose}
|
|
|
|
|
|
onDoNotShowToday={handleDoNotShowToday}
|
2025-11-27 17:29:23 +08:00
|
|
|
|
onNeverShow={handleNeverShow}
|
2025-10-30 11:14:43 +08:00
|
|
|
|
/>
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-11 19:50:12 +08:00
|
|
|
|
<Modal
|
|
|
|
|
|
title="设置账号密码"
|
|
|
|
|
|
open={showPasswordModal}
|
|
|
|
|
|
centered
|
|
|
|
|
|
onOk={handleSetPassword}
|
|
|
|
|
|
onCancel={handleSkipPasswordSetting}
|
|
|
|
|
|
confirmLoading={settingPassword}
|
|
|
|
|
|
okText="设置密码"
|
|
|
|
|
|
cancelText="暂不设置"
|
|
|
|
|
|
width={500}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div style={{ marginBottom: 20 }}>
|
|
|
|
|
|
<p>您已成功通过 Linux DO 授权登录!</p>
|
|
|
|
|
|
<p>系统已为您自动生成默认密码,您可以选择设置自定义密码或继续使用默认密码。</p>
|
|
|
|
|
|
{passwordStatus?.default_password && (
|
|
|
|
|
|
<div style={{
|
|
|
|
|
|
background: '#f0f2f5',
|
|
|
|
|
|
padding: 12,
|
|
|
|
|
|
borderRadius: 4,
|
|
|
|
|
|
marginTop: 12
|
|
|
|
|
|
}}>
|
2025-12-15 16:38:41 +08:00
|
|
|
|
<strong>账号:</strong>{passwordStatus.username}<br />
|
2025-11-11 19:50:12 +08:00
|
|
|
|
<strong>默认密码:</strong><code style={{
|
|
|
|
|
|
background: '#fff',
|
|
|
|
|
|
padding: '2px 8px',
|
|
|
|
|
|
borderRadius: 3,
|
|
|
|
|
|
color: '#1890ff',
|
|
|
|
|
|
fontSize: 14
|
|
|
|
|
|
}}>{passwordStatus.default_password}</code>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2025-12-15 16:38:41 +08:00
|
|
|
|
|
2025-11-11 19:50:12 +08:00
|
|
|
|
<div style={{ marginTop: 20 }}>
|
|
|
|
|
|
<div style={{ marginBottom: 12 }}>
|
|
|
|
|
|
<label>新密码(至少6个字符):</label>
|
|
|
|
|
|
<Input.Password
|
|
|
|
|
|
value={newPassword}
|
|
|
|
|
|
onChange={(e) => setNewPassword(e.target.value)}
|
|
|
|
|
|
placeholder="请输入新密码"
|
|
|
|
|
|
style={{ marginTop: 4 }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label>确认密码:</label>
|
|
|
|
|
|
<Input.Password
|
|
|
|
|
|
value={confirmPassword}
|
|
|
|
|
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
|
|
|
|
|
placeholder="请再次输入密码"
|
|
|
|
|
|
style={{ marginTop: 4 }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
|
2025-11-06 12:36:32 +08:00
|
|
|
|
<div style={{
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
minHeight: '100vh',
|
2025-12-15 16:38:41 +08:00
|
|
|
|
background: 'linear-gradient(135deg, #4D8088 0%, #5F9EA8 100%)',
|
2025-11-06 12:36:32 +08:00
|
|
|
|
}}>
|
|
|
|
|
|
<Result
|
|
|
|
|
|
status="success"
|
|
|
|
|
|
title="登录成功"
|
2025-11-11 19:50:12 +08:00
|
|
|
|
subTitle={showPasswordModal ? "请设置账号密码..." : (showAnnouncement ? "欢迎使用..." : "正在跳转...")}
|
2025-11-06 12:36:32 +08:00
|
|
|
|
style={{ background: 'white', padding: 40, borderRadius: 8 }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</>
|
2025-10-30 11:14:43 +08:00
|
|
|
|
);
|
|
|
|
|
|
}
|