mirror of
https://github.com/linux-do/new-api.git
synced 2025-11-12 17:13:43 +08:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # web/src/components/PersonalSetting.js
This commit is contained in:
@@ -13,8 +13,8 @@ const GitHubOAuth = () => {
|
||||
|
||||
let navigate = useNavigate();
|
||||
|
||||
const sendCode = async (code, count) => {
|
||||
const res = await API.get(`/api/oauth/github?code=${code}`);
|
||||
const sendCode = async (code, state, count) => {
|
||||
const res = await API.get(`/api/oauth/github?code=${code}&state=${state}`);
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
if (message === 'bind') {
|
||||
@@ -36,13 +36,14 @@ const GitHubOAuth = () => {
|
||||
count++;
|
||||
setPrompt(`出现错误,第 ${count} 次重试中...`);
|
||||
await new Promise((resolve) => setTimeout(resolve, count * 2000));
|
||||
await sendCode(code, count);
|
||||
await sendCode(code, state, count);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let code = searchParams.get('code');
|
||||
sendCode(code, 0).then();
|
||||
let state = searchParams.get('state');
|
||||
sendCode(code, state, 0).then();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button, Divider, Form, Grid, Header, Image, Message, Modal, Segment } f
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { UserContext } from '../context/User';
|
||||
import { API, getLogo, showError, showSuccess } from '../helpers';
|
||||
import { getOAuthState, onGitHubOAuthClicked } from './utils';
|
||||
|
||||
const LoginForm = () => {
|
||||
const [inputs, setInputs] = useState({
|
||||
@@ -31,12 +32,6 @@ const LoginForm = () => {
|
||||
|
||||
const [showWeChatLoginModal, setShowWeChatLoginModal] = useState(false);
|
||||
|
||||
const onGitHubOAuthClicked = () => {
|
||||
window.open(
|
||||
`https://github.com/login/oauth/authorize?client_id=${status.github_client_id}&scope=user:email`
|
||||
);
|
||||
};
|
||||
|
||||
const onWeChatLoginClicked = () => {
|
||||
setShowWeChatLoginModal(true);
|
||||
};
|
||||
@@ -131,7 +126,7 @@ const LoginForm = () => {
|
||||
circular
|
||||
color='black'
|
||||
icon='github'
|
||||
onClick={onGitHubOAuthClicked}
|
||||
onClick={()=>onGitHubOAuthClicked(status.github_client_id)}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
|
||||
@@ -4,378 +4,373 @@ import { Link, useNavigate } from 'react-router-dom';
|
||||
import { API, copy, showError, showInfo, showNotice, showSuccess } from '../helpers';
|
||||
import Turnstile from 'react-turnstile';
|
||||
import { UserContext } from '../context/User';
|
||||
import { onGitHubOAuthClicked } from './utils';
|
||||
|
||||
const PersonalSetting = () => {
|
||||
const [userState, userDispatch] = useContext(UserContext);
|
||||
let navigate = useNavigate();
|
||||
const [userState, userDispatch] = useContext(UserContext);
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [inputs, setInputs] = useState({
|
||||
wechat_verification_code: '',
|
||||
email_verification_code: '',
|
||||
email: '',
|
||||
self_account_deletion_confirmation: ''
|
||||
});
|
||||
const [status, setStatus] = useState({});
|
||||
const [showWeChatBindModal, setShowWeChatBindModal] = useState(false);
|
||||
const [showEmailBindModal, setShowEmailBindModal] = useState(false);
|
||||
const [showAccountDeleteModal, setShowAccountDeleteModal] = useState(false);
|
||||
const [turnstileEnabled, setTurnstileEnabled] = useState(false);
|
||||
const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
|
||||
const [turnstileToken, setTurnstileToken] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [disableButton, setDisableButton] = useState(false);
|
||||
const [countdown, setCountdown] = useState(30);
|
||||
const [affLink, setAffLink] = useState("");
|
||||
const [systemToken, setSystemToken] = useState("");
|
||||
const [inputs, setInputs] = useState({
|
||||
wechat_verification_code: '',
|
||||
email_verification_code: '',
|
||||
email: '',
|
||||
self_account_deletion_confirmation: ''
|
||||
});
|
||||
const [status, setStatus] = useState({});
|
||||
const [showWeChatBindModal, setShowWeChatBindModal] = useState(false);
|
||||
const [showEmailBindModal, setShowEmailBindModal] = useState(false);
|
||||
const [showAccountDeleteModal, setShowAccountDeleteModal] = useState(false);
|
||||
const [turnstileEnabled, setTurnstileEnabled] = useState(false);
|
||||
const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
|
||||
const [turnstileToken, setTurnstileToken] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [disableButton, setDisableButton] = useState(false);
|
||||
const [countdown, setCountdown] = useState(30);
|
||||
const [affLink, setAffLink] = useState("");
|
||||
const [systemToken, setSystemToken] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
let status = localStorage.getItem('status');
|
||||
if (status) {
|
||||
status = JSON.parse(status);
|
||||
setStatus(status);
|
||||
if (status.turnstile_check) {
|
||||
setTurnstileEnabled(true);
|
||||
setTurnstileSiteKey(status.turnstile_site_key);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
let status = localStorage.getItem('status');
|
||||
if (status) {
|
||||
status = JSON.parse(status);
|
||||
setStatus(status);
|
||||
if (status.turnstile_check) {
|
||||
setTurnstileEnabled(true);
|
||||
setTurnstileSiteKey(status.turnstile_site_key);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let countdownInterval = null;
|
||||
if (disableButton && countdown > 0) {
|
||||
countdownInterval = setInterval(() => {
|
||||
setCountdown(countdown - 1);
|
||||
}, 1000);
|
||||
} else if (countdown === 0) {
|
||||
setDisableButton(false);
|
||||
setCountdown(30);
|
||||
}
|
||||
return () => clearInterval(countdownInterval); // Clean up on unmount
|
||||
}, [disableButton, countdown]);
|
||||
useEffect(() => {
|
||||
let countdownInterval = null;
|
||||
if (disableButton && countdown > 0) {
|
||||
countdownInterval = setInterval(() => {
|
||||
setCountdown(countdown - 1);
|
||||
}, 1000);
|
||||
} else if (countdown === 0) {
|
||||
setDisableButton(false);
|
||||
setCountdown(30);
|
||||
}
|
||||
return () => clearInterval(countdownInterval); // Clean up on unmount
|
||||
}, [disableButton, countdown]);
|
||||
|
||||
const handleInputChange = (e, { name, value }) => {
|
||||
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
||||
};
|
||||
const handleInputChange = (e, { name, value }) => {
|
||||
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
||||
};
|
||||
|
||||
const generateAccessToken = async () => {
|
||||
const res = await API.get('/api/user/token');
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
setSystemToken(data);
|
||||
setAffLink("");
|
||||
await copy(data);
|
||||
showSuccess(`令牌已重置并已复制到剪贴板`);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
};
|
||||
const generateAccessToken = async () => {
|
||||
const res = await API.get('/api/user/token');
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
setSystemToken(data);
|
||||
setAffLink("");
|
||||
await copy(data);
|
||||
showSuccess(`令牌已重置并已复制到剪贴板`);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
};
|
||||
|
||||
const getAffLink = async () => {
|
||||
const res = await API.get('/api/user/aff');
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
let link = `${window.location.origin}/register?aff=${data}`;
|
||||
setAffLink(link);
|
||||
setSystemToken("");
|
||||
await copy(link);
|
||||
showSuccess(`邀请链接已复制到剪切板`);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
};
|
||||
const getAffLink = async () => {
|
||||
const res = await API.get('/api/user/aff');
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
let link = `${window.location.origin}/register?aff=${data}`;
|
||||
setAffLink(link);
|
||||
setSystemToken("");
|
||||
await copy(link);
|
||||
showSuccess(`邀请链接已复制到剪切板`);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAffLinkClick = async (e) => {
|
||||
e.target.select();
|
||||
await copy(e.target.value);
|
||||
showSuccess(`邀请链接已复制到剪切板`);
|
||||
};
|
||||
const handleAffLinkClick = async (e) => {
|
||||
e.target.select();
|
||||
await copy(e.target.value);
|
||||
showSuccess(`邀请链接已复制到剪切板`);
|
||||
};
|
||||
|
||||
const handleSystemTokenClick = async (e) => {
|
||||
e.target.select();
|
||||
await copy(e.target.value);
|
||||
showSuccess(`系统令牌已复制到剪切板`);
|
||||
};
|
||||
const handleSystemTokenClick = async (e) => {
|
||||
e.target.select();
|
||||
await copy(e.target.value);
|
||||
showSuccess(`系统令牌已复制到剪切板`);
|
||||
};
|
||||
|
||||
const deleteAccount = async () => {
|
||||
if (inputs.self_account_deletion_confirmation !== userState.user.username) {
|
||||
showError('请输入你的账户名以确认删除!');
|
||||
return;
|
||||
}
|
||||
const deleteAccount = async () => {
|
||||
if (inputs.self_account_deletion_confirmation !== userState.user.username) {
|
||||
showError('请输入你的账户名以确认删除!');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await API.delete('/api/user/self');
|
||||
const { success, message } = res.data;
|
||||
const res = await API.delete('/api/user/self');
|
||||
const { success, message } = res.data;
|
||||
|
||||
if (success) {
|
||||
showSuccess('账户已删除!');
|
||||
await API.get('/api/user/logout');
|
||||
userDispatch({ type: 'logout' });
|
||||
localStorage.removeItem('user');
|
||||
navigate('/login');
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
};
|
||||
if (success) {
|
||||
showSuccess('账户已删除!');
|
||||
await API.get('/api/user/logout');
|
||||
userDispatch({ type: 'logout' });
|
||||
localStorage.removeItem('user');
|
||||
navigate('/login');
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
};
|
||||
|
||||
const bindWeChat = async () => {
|
||||
if (inputs.wechat_verification_code === '') return;
|
||||
const res = await API.get(
|
||||
`/api/oauth/wechat/bind?code=${inputs.wechat_verification_code}`
|
||||
);
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess('微信账户绑定成功!');
|
||||
setShowWeChatBindModal(false);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
};
|
||||
|
||||
const openGitHubOAuth = () => {
|
||||
window.open(
|
||||
`https://github.com/login/oauth/authorize?client_id=${status.github_client_id}&scope=user:email`
|
||||
);
|
||||
};
|
||||
|
||||
const sendVerificationCode = async () => {
|
||||
setDisableButton(true);
|
||||
if (inputs.email === '') return;
|
||||
if (turnstileEnabled && turnstileToken === '') {
|
||||
showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const res = await API.get(
|
||||
`/api/verification?email=${inputs.email}&turnstile=${turnstileToken}`
|
||||
);
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess('验证码发送成功,请检查邮箱!');
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const bindEmail = async () => {
|
||||
if (inputs.email_verification_code === '') return;
|
||||
setLoading(true);
|
||||
const res = await API.get(
|
||||
`/api/oauth/email/bind?email=${inputs.email}&code=${inputs.email_verification_code}`
|
||||
);
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess('邮箱账户绑定成功!');
|
||||
setShowEmailBindModal(false);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ lineHeight: '40px' }}>
|
||||
<Header as='h3'>通用设置</Header>
|
||||
<Message>
|
||||
注意,此处生成的令牌用于系统管理,而非用于请求 OpenAI 相关的服务,请知悉。
|
||||
</Message>
|
||||
<Button as={Link} to={`/user/edit/`}>
|
||||
更新个人信息
|
||||
</Button>
|
||||
<Button onClick={generateAccessToken}>生成系统访问令牌</Button>
|
||||
<Button onClick={getAffLink}>复制邀请链接</Button>
|
||||
<Button onClick={() => {
|
||||
setShowAccountDeleteModal(true);
|
||||
}}>删除个人账户</Button>
|
||||
|
||||
{systemToken && (
|
||||
<Form.Input
|
||||
fluid
|
||||
readOnly
|
||||
value={systemToken}
|
||||
onClick={handleSystemTokenClick}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
)}
|
||||
{affLink && (
|
||||
<Form.Input
|
||||
fluid
|
||||
readOnly
|
||||
value={affLink}
|
||||
onClick={handleAffLinkClick}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
)}
|
||||
<Divider />
|
||||
<Header as='h3'>账号绑定</Header>
|
||||
{
|
||||
status.wechat_login && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShowWeChatBindModal(true);
|
||||
}}
|
||||
>
|
||||
绑定微信账号
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
<Modal
|
||||
onClose={() => setShowWeChatBindModal(false)}
|
||||
onOpen={() => setShowWeChatBindModal(true)}
|
||||
open={showWeChatBindModal}
|
||||
size={'mini'}
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Description>
|
||||
<Image src={status.wechat_qrcode} fluid />
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<p>
|
||||
微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)
|
||||
</p>
|
||||
</div>
|
||||
<Form size='large'>
|
||||
<Form.Input
|
||||
fluid
|
||||
placeholder='验证码'
|
||||
name='wechat_verification_code'
|
||||
value={inputs.wechat_verification_code}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Button color='' fluid size='large' onClick={bindWeChat}>
|
||||
绑定
|
||||
</Button>
|
||||
</Form>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
{
|
||||
status.github_oauth && (
|
||||
<Button onClick={openGitHubOAuth}>绑定 GitHub 账号</Button>
|
||||
)
|
||||
}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShowEmailBindModal(true);
|
||||
}}
|
||||
>
|
||||
绑定邮箱地址
|
||||
</Button>
|
||||
<Modal
|
||||
onClose={() => setShowEmailBindModal(false)}
|
||||
onOpen={() => setShowEmailBindModal(true)}
|
||||
open={showEmailBindModal}
|
||||
size={'tiny'}
|
||||
style={{ maxWidth: '450px' }}
|
||||
>
|
||||
<Modal.Header>绑定邮箱地址</Modal.Header>
|
||||
<Modal.Content>
|
||||
<Modal.Description>
|
||||
<Form size='large'>
|
||||
<Form.Input
|
||||
fluid
|
||||
placeholder='输入邮箱地址'
|
||||
onChange={handleInputChange}
|
||||
name='email'
|
||||
type='email'
|
||||
action={
|
||||
<Button onClick={sendVerificationCode} disabled={disableButton || loading}>
|
||||
{disableButton ? `重新发送(${countdown})` : '获取验证码'}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<Form.Input
|
||||
fluid
|
||||
placeholder='验证码'
|
||||
name='email_verification_code'
|
||||
value={inputs.email_verification_code}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
{turnstileEnabled ? (
|
||||
<Turnstile
|
||||
sitekey={turnstileSiteKey}
|
||||
onVerify={(token) => {
|
||||
setTurnstileToken(token);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}>
|
||||
<Button
|
||||
color=''
|
||||
fluid
|
||||
size='large'
|
||||
onClick={bindEmail}
|
||||
loading={loading}
|
||||
>
|
||||
确认绑定
|
||||
</Button>
|
||||
<div style={{ width: '1rem' }}></div>
|
||||
<Button
|
||||
fluid
|
||||
size='large'
|
||||
onClick={() => setShowEmailBindModal(false)}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
<Modal
|
||||
onClose={() => setShowAccountDeleteModal(false)}
|
||||
onOpen={() => setShowAccountDeleteModal(true)}
|
||||
open={showAccountDeleteModal}
|
||||
size={'tiny'}
|
||||
style={{ maxWidth: '450px' }}
|
||||
>
|
||||
<Modal.Header>危险操作</Modal.Header>
|
||||
<Modal.Content>
|
||||
<Message>您正在删除自己的帐户,将清空所有数据且不可恢复</Message>
|
||||
<Modal.Description>
|
||||
<Form size='large'>
|
||||
<Form.Input
|
||||
fluid
|
||||
placeholder={`输入你的账户名 ${userState?.user?.username} 以确认删除`}
|
||||
name='self_account_deletion_confirmation'
|
||||
value={inputs.self_account_deletion_confirmation}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
{turnstileEnabled ? (
|
||||
<Turnstile
|
||||
sitekey={turnstileSiteKey}
|
||||
onVerify={(token) => {
|
||||
setTurnstileToken(token);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}>
|
||||
<Button
|
||||
color='red'
|
||||
fluid
|
||||
size='large'
|
||||
onClick={deleteAccount}
|
||||
loading={loading}
|
||||
>
|
||||
确认删除
|
||||
</Button>
|
||||
<div style={{ width: '1rem' }}></div>
|
||||
<Button
|
||||
fluid
|
||||
size='large'
|
||||
onClick={() => setShowAccountDeleteModal(false)}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
</div>
|
||||
const bindWeChat = async () => {
|
||||
if (inputs.wechat_verification_code === '') return;
|
||||
const res = await API.get(
|
||||
`/api/oauth/wechat/bind?code=${inputs.wechat_verification_code}`
|
||||
);
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess('微信账户绑定成功!');
|
||||
setShowWeChatBindModal(false);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
};
|
||||
|
||||
const sendVerificationCode = async () => {
|
||||
setDisableButton(true);
|
||||
if (inputs.email === '') return;
|
||||
if (turnstileEnabled && turnstileToken === '') {
|
||||
showInfo('请稍后几秒重试,Turnstile 正在检查用户环境!');
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
const res = await API.get(
|
||||
`/api/verification?email=${inputs.email}&turnstile=${turnstileToken}`
|
||||
);
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess('验证码发送成功,请检查邮箱!');
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const bindEmail = async () => {
|
||||
if (inputs.email_verification_code === '') return;
|
||||
setLoading(true);
|
||||
const res = await API.get(
|
||||
`/api/oauth/email/bind?email=${inputs.email}&code=${inputs.email_verification_code}`
|
||||
);
|
||||
const { success, message } = res.data;
|
||||
if (success) {
|
||||
showSuccess('邮箱账户绑定成功!');
|
||||
setShowEmailBindModal(false);
|
||||
} else {
|
||||
showError(message);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ lineHeight: '40px' }}>
|
||||
<Header as='h3'>通用设置</Header>
|
||||
<Message>
|
||||
注意,此处生成的令牌用于系统管理,而非用于请求 OpenAI 相关的服务,请知悉。
|
||||
</Message>
|
||||
<Button as={Link} to={`/user/edit/`}>
|
||||
更新个人信息
|
||||
</Button>
|
||||
<Button onClick={generateAccessToken}>生成系统访问令牌</Button>
|
||||
<Button onClick={getAffLink}>复制邀请链接</Button>
|
||||
<Button onClick={() => {
|
||||
setShowAccountDeleteModal(true);
|
||||
}}>删除个人账户</Button>
|
||||
|
||||
{systemToken && (
|
||||
<Form.Input
|
||||
fluid
|
||||
readOnly
|
||||
value={systemToken}
|
||||
onClick={handleSystemTokenClick}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
)}
|
||||
{affLink && (
|
||||
<Form.Input
|
||||
fluid
|
||||
readOnly
|
||||
value={affLink}
|
||||
onClick={handleAffLinkClick}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
)}
|
||||
<Divider />
|
||||
<Header as='h3'>账号绑定</Header>
|
||||
{
|
||||
status.wechat_login && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShowWeChatBindModal(true);
|
||||
}}
|
||||
>
|
||||
绑定微信账号
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
<Modal
|
||||
onClose={() => setShowWeChatBindModal(false)}
|
||||
onOpen={() => setShowWeChatBindModal(true)}
|
||||
open={showWeChatBindModal}
|
||||
size={'mini'}
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Description>
|
||||
<Image src={status.wechat_qrcode} fluid />
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<p>
|
||||
微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)
|
||||
</p>
|
||||
</div>
|
||||
<Form size='large'>
|
||||
<Form.Input
|
||||
fluid
|
||||
placeholder='验证码'
|
||||
name='wechat_verification_code'
|
||||
value={inputs.wechat_verification_code}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<Button color='' fluid size='large' onClick={bindWeChat}>
|
||||
绑定
|
||||
</Button>
|
||||
</Form>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
{
|
||||
status.github_oauth && (
|
||||
<Button onClick={()=>{onGitHubOAuthClicked(status.github_client_id)}}>绑定 GitHub 账号</Button>
|
||||
)
|
||||
}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShowEmailBindModal(true);
|
||||
}}
|
||||
>
|
||||
绑定邮箱地址
|
||||
</Button>
|
||||
<Modal
|
||||
onClose={() => setShowEmailBindModal(false)}
|
||||
onOpen={() => setShowEmailBindModal(true)}
|
||||
open={showEmailBindModal}
|
||||
size={'tiny'}
|
||||
style={{ maxWidth: '450px' }}
|
||||
>
|
||||
<Modal.Header>绑定邮箱地址</Modal.Header>
|
||||
<Modal.Content>
|
||||
<Modal.Description>
|
||||
<Form size='large'>
|
||||
<Form.Input
|
||||
fluid
|
||||
placeholder='输入邮箱地址'
|
||||
onChange={handleInputChange}
|
||||
name='email'
|
||||
type='email'
|
||||
action={
|
||||
<Button onClick={sendVerificationCode} disabled={disableButton || loading}>
|
||||
{disableButton ? `重新发送(${countdown})` : '获取验证码'}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<Form.Input
|
||||
fluid
|
||||
placeholder='验证码'
|
||||
name='email_verification_code'
|
||||
value={inputs.email_verification_code}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
{turnstileEnabled ? (
|
||||
<Turnstile
|
||||
sitekey={turnstileSiteKey}
|
||||
onVerify={(token) => {
|
||||
setTurnstileToken(token);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}>
|
||||
<Button
|
||||
color=''
|
||||
fluid
|
||||
size='large'
|
||||
onClick={bindEmail}
|
||||
loading={loading}
|
||||
>
|
||||
确认绑定
|
||||
</Button>
|
||||
<div style={{ width: '1rem' }}></div>
|
||||
<Button
|
||||
fluid
|
||||
size='large'
|
||||
onClick={() => setShowEmailBindModal(false)}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
<Modal
|
||||
onClose={() => setShowAccountDeleteModal(false)}
|
||||
onOpen={() => setShowAccountDeleteModal(true)}
|
||||
open={showAccountDeleteModal}
|
||||
size={'tiny'}
|
||||
style={{ maxWidth: '450px' }}
|
||||
>
|
||||
<Modal.Header>危险操作</Modal.Header>
|
||||
<Modal.Content>
|
||||
<Message>您正在删除自己的帐户,将清空所有数据且不可恢复</Message>
|
||||
<Modal.Description>
|
||||
<Form size='large'>
|
||||
<Form.Input
|
||||
fluid
|
||||
placeholder={`输入你的账户名 ${userState?.user?.username} 以确认删除`}
|
||||
name='self_account_deletion_confirmation'
|
||||
value={inputs.self_account_deletion_confirmation}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
{turnstileEnabled ? (
|
||||
<Turnstile
|
||||
sitekey={turnstileSiteKey}
|
||||
onVerify={(token) => {
|
||||
setTurnstileToken(token);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '1rem' }}>
|
||||
<Button
|
||||
color='red'
|
||||
fluid
|
||||
size='large'
|
||||
onClick={deleteAccount}
|
||||
loading={loading}
|
||||
>
|
||||
确认删除
|
||||
</Button>
|
||||
<div style={{ width: '1rem' }}></div>
|
||||
<Button
|
||||
fluid
|
||||
size='large'
|
||||
onClick={() => setShowAccountDeleteModal(false)}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal.Description>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PersonalSetting;
|
||||
export default PersonalSetting;
|
||||
|
||||
20
web/src/components/utils.js
Normal file
20
web/src/components/utils.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { API, showError } from '../helpers';
|
||||
|
||||
export async function getOAuthState() {
|
||||
const res = await API.get('/api/oauth/state');
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
return data;
|
||||
} else {
|
||||
showError(message);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export async function onGitHubOAuthClicked(github_client_id) {
|
||||
const state = await getOAuthState();
|
||||
if (!state) return;
|
||||
window.open(
|
||||
`https://github.com/login/oauth/authorize?client_id=${github_client_id}&state=${state}&scope=user:email`
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user