mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-06-08 08:59:36 +08:00
feat: implement account binding functionality for GitHub and LinuxDo
This commit is contained in:
@@ -251,6 +251,29 @@ const ConfigTabs: React.FC<ConfigTabsProps> = ({
|
||||
</Form>
|
||||
</ConfigSection>
|
||||
</TabPane>
|
||||
<TabPane tab="LinuxDo认证" key="linuxdo">
|
||||
<ConfigSection
|
||||
title="LinuxDo OAuth 配置"
|
||||
icon={<GlobalOutlined />}
|
||||
description="LinuxDo OAuth 应用配置,用于实现第三方登录功能"
|
||||
isMobile={isMobile}
|
||||
>
|
||||
<Form form={formsMap.Authentication} layout="vertical" size={isMobile ? "middle" : "large"}>
|
||||
{renderConfigFormItems(formsMap.Authentication, "Authentication", ["LinuxDoClientId", "LinuxDoClientSecret", "LinuxDoCallbackUrl"])}
|
||||
<Divider style={{ margin: '12px 0 20px' }} />
|
||||
<Form.Item style={{ marginBottom: 0, textAlign: 'center' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SaveOutlined />}
|
||||
onClick={() => onSaveAllForGroup(formsMap.Authentication, "Authentication", ["LinuxDoClientId", "LinuxDoClientSecret", "LinuxDoCallbackUrl"])}
|
||||
style={{ width: isMobile ? '100%' : '240px' }}
|
||||
>
|
||||
保存所有 LinuxDo 认证配置
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</ConfigSection>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -37,7 +37,10 @@ const allDescriptions: Record<string, Record<string, string>> = {
|
||||
Authentication: {
|
||||
GitHubClientId: 'GitHub OAuth 应用客户端ID',
|
||||
GitHubClientSecret: 'GitHub OAuth 应用客户端密钥',
|
||||
GitHubCallbackUrl: 'GitHub OAuth 认证回调地址'
|
||||
GitHubCallbackUrl: 'GitHub OAuth 认证回调地址',
|
||||
LinuxDoClientId: 'LinuxDo OAuth 应用客户端ID',
|
||||
LinuxDoClientSecret: 'LinuxDo OAuth 应用客户端密钥',
|
||||
LinuxDoCallbackUrl: 'LinuxDo OAuth 认证回调地址'
|
||||
},
|
||||
AppSettings: {
|
||||
ServerUrl: '服务器URL'
|
||||
|
||||
214
Web/src/pages/bind/Index.tsx
Normal file
214
Web/src/pages/bind/Index.tsx
Normal file
@@ -0,0 +1,214 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Form, Input, Button, Typography, Row, Col, Card, message, Alert } from 'antd';
|
||||
import { UserOutlined, LockOutlined, GithubOutlined, LinkOutlined } from '@ant-design/icons';
|
||||
import { useNavigate, useSearchParams } from 'react-router';
|
||||
import { bindAccount, BindType } from '../../api';
|
||||
import useIsMobile from '../../hooks/useIsMobile';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
const Bind: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const githubId = searchParams.get('githubId');
|
||||
const linuxdoId = searchParams.get('linuxdoId');
|
||||
const thirdPartyUserId = githubId || linuxdoId;
|
||||
const bindType = githubId ? BindType.GitHub : BindType.LinuxDo;
|
||||
|
||||
useEffect(() => {
|
||||
// 检查是否有必要的参数
|
||||
if (!thirdPartyUserId) {
|
||||
message.error('缺少必要的绑定参数');
|
||||
navigate('/login');
|
||||
}
|
||||
}, [thirdPartyUserId, navigate]);
|
||||
|
||||
const onFinish = async (values: any) => {
|
||||
if (!thirdPartyUserId) {
|
||||
message.error('缺少第三方用户ID');
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await bindAccount({
|
||||
email: values.email,
|
||||
password: values.password,
|
||||
bindType: bindType,
|
||||
thirdPartyUserId: thirdPartyUserId
|
||||
});
|
||||
|
||||
if (response.success && response.data) {
|
||||
message.success(response.message || '账户绑定成功!');
|
||||
navigate('/');
|
||||
} else {
|
||||
message.error(response.message || '绑定失败,请检查邮箱和密码');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('绑定出错:', error);
|
||||
message.error('绑定过程中出现错误,请稍后重试');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getBindTypeIcon = () => {
|
||||
switch (bindType) {
|
||||
case BindType.GitHub:
|
||||
return <GithubOutlined style={{ fontSize: '24px', color: '#24292e' }} />;
|
||||
case BindType.LinuxDo:
|
||||
return <img src="/images/linuxdo.svg" alt="LinuxDo" style={{ width: '32px', height: '32px' }} />;
|
||||
default:
|
||||
return <LinkOutlined style={{ fontSize: '24px' }} />;
|
||||
}
|
||||
};
|
||||
|
||||
const getBindTypeText = () => {
|
||||
switch (bindType) {
|
||||
case BindType.GitHub:
|
||||
return 'GitHub';
|
||||
case BindType.LinuxDo:
|
||||
return 'LinuxDo';
|
||||
default:
|
||||
return '第三方';
|
||||
}
|
||||
};
|
||||
|
||||
if (!thirdPartyUserId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Row style={{ minHeight: '100vh', backgroundColor: '#f5f5f5', padding: isMobile ? '20px' : '40px' }}>
|
||||
<Col
|
||||
xs={24}
|
||||
sm={20}
|
||||
md={16}
|
||||
lg={12}
|
||||
xl={8}
|
||||
style={{
|
||||
margin: '0 auto',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
style={{
|
||||
width: '100%',
|
||||
maxWidth: '500px',
|
||||
borderRadius: '12px',
|
||||
boxShadow: '0 4px 16px rgba(0,0,0,0.1)',
|
||||
border: 'none'
|
||||
}}
|
||||
bodyStyle={{ padding: isMobile ? '24px' : '40px' }}
|
||||
>
|
||||
<div style={{ textAlign: 'center', marginBottom: '32px' }}>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
{getBindTypeIcon()}
|
||||
</div>
|
||||
<Title level={2} style={{
|
||||
marginBottom: '8px',
|
||||
fontWeight: 700,
|
||||
color: '#18181b'
|
||||
}}>
|
||||
绑定{getBindTypeText()}账户
|
||||
</Title>
|
||||
<Text style={{ fontSize: '16px', color: '#666' }}>
|
||||
请输入您的Foxel账户信息来绑定{getBindTypeText()}账户
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<Alert
|
||||
message="账户绑定说明"
|
||||
description={
|
||||
<div>
|
||||
<p>• 如果您已有Foxel账户,请输入邮箱和密码进行绑定</p>
|
||||
<p>• 如果您还没有Foxel账户,系统将自动为您创建一个新账户</p>
|
||||
<p>• 绑定后您可以使用{getBindTypeText()}账户快速登录</p>
|
||||
</div>
|
||||
}
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: '24px' }}
|
||||
/>
|
||||
|
||||
<Form
|
||||
name="bind_form"
|
||||
onFinish={onFinish}
|
||||
size="large"
|
||||
layout="vertical"
|
||||
>
|
||||
<Form.Item
|
||||
label="邮箱"
|
||||
name="email"
|
||||
rules={[
|
||||
{ required: true, message: '请输入您的邮箱' },
|
||||
{ type: 'email', message: '请输入有效的邮箱地址' }
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
prefix={<UserOutlined style={{ color: '#bfbfbf' }} />}
|
||||
placeholder="请输入邮箱地址"
|
||||
style={{
|
||||
height: '48px',
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="密码"
|
||||
name="password"
|
||||
rules={[
|
||||
{ required: true, message: '请输入您的密码' },
|
||||
{ min: 6, message: '密码长度不能少于6位' }
|
||||
]}
|
||||
>
|
||||
<Input.Password
|
||||
prefix={<LockOutlined style={{ color: '#bfbfbf' }} />}
|
||||
placeholder="请输入密码(6位以上)"
|
||||
style={{
|
||||
height: '48px',
|
||||
borderRadius: '8px'
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item style={{ marginBottom: '16px' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={loading}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '48px',
|
||||
borderRadius: '8px',
|
||||
fontWeight: 500,
|
||||
fontSize: '16px'
|
||||
}}
|
||||
>
|
||||
{loading ? '绑定中...' : `绑定${getBindTypeText()}账户`}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => navigate('/login')}
|
||||
style={{ padding: '0', color: '#666' }}
|
||||
>
|
||||
返回登录页面
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default Bind;
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import {Form, Input, Button, Checkbox, Typography, Row, Col, Divider, message} from 'antd';
|
||||
import {UserOutlined, LockOutlined, GithubOutlined, GoogleOutlined} from '@ant-design/icons';
|
||||
import {UserOutlined, LockOutlined, GithubOutlined} from '@ant-design/icons';
|
||||
import {useNavigate, Link} from 'react-router';
|
||||
import {login, saveAuthData, isAuthenticated, handleOAuthCallback, getGitHubLoginUrl} from '../../api';
|
||||
import {login, saveAuthData, isAuthenticated, handleOAuthCallback, getGitHubLoginUrl, getLinuxDoLoginUrl} from '../../api';
|
||||
import useIsMobile from '../../hooks/useIsMobile';
|
||||
|
||||
const {Title, Text} = Typography;
|
||||
@@ -67,6 +67,10 @@ const Login: React.FC = () => {
|
||||
window.location.href = getGitHubLoginUrl();
|
||||
};
|
||||
|
||||
const handleLinuxDoLogin = () => {
|
||||
window.location.href = getLinuxDoLoginUrl();
|
||||
};
|
||||
|
||||
return (
|
||||
<Row style={{height: '100vh', overflow: 'hidden'}}>
|
||||
{/* 左侧登录表单 */}
|
||||
@@ -184,9 +188,10 @@ const Login: React.FC = () => {
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={<GoogleOutlined/>}
|
||||
icon={<img src="/images/linuxdo.svg" alt="LinuxDo" style={{width: '20px', height: '20px'}} />}
|
||||
size="large"
|
||||
shape="circle"
|
||||
onClick={handleLinuxDoLogin}
|
||||
style={{
|
||||
backgroundColor: '#f6f6f6',
|
||||
border: 'none',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Form, Input, Button, Checkbox, Typography, Row, Col, Divider, message } from 'antd';
|
||||
import { UserOutlined, LockOutlined, MailOutlined, GithubOutlined, GoogleOutlined } from '@ant-design/icons';
|
||||
import { UserOutlined, LockOutlined, MailOutlined, GithubOutlined } from '@ant-design/icons';
|
||||
import { useNavigate, Link } from 'react-router';
|
||||
import { register, saveAuthData, isAuthenticated, handleOAuthCallback, getGitHubLoginUrl } from '../../api';
|
||||
import { register, saveAuthData, isAuthenticated, handleOAuthCallback, getGitHubLoginUrl, getLinuxDoLoginUrl } from '../../api';
|
||||
import useIsMobile from '../../hooks/useIsMobile';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
@@ -68,6 +68,10 @@ const Register: React.FC = () => {
|
||||
window.location.href = getGitHubLoginUrl();
|
||||
};
|
||||
|
||||
const handleLinuxDoLogin = () => {
|
||||
window.location.href = getLinuxDoLoginUrl();
|
||||
};
|
||||
|
||||
return (
|
||||
<Row style={{ height: '100vh', overflow: 'hidden' }}>
|
||||
{/* 左侧注册表单 */}
|
||||
@@ -245,9 +249,10 @@ const Register: React.FC = () => {
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={<GoogleOutlined />}
|
||||
icon={<img src="/images/linuxdo.svg" alt="LinuxDo" style={{width: '20px', height: '20px'}} />}
|
||||
size="large"
|
||||
shape="circle"
|
||||
onClick={handleLinuxDoLogin}
|
||||
style={{
|
||||
backgroundColor: '#f6f6f6',
|
||||
border: 'none',
|
||||
|
||||
Reference in New Issue
Block a user