diff --git a/web/src/assets/svg/sso.svg b/web/src/assets/svg/sso.svg new file mode 100644 index 000000000..0925d4fd1 --- /dev/null +++ b/web/src/assets/svg/sso.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/web/src/hooks/auth-hooks.ts b/web/src/hooks/auth-hooks.ts index f1081d683..208e87dea 100644 --- a/web/src/hooks/auth-hooks.ts +++ b/web/src/hooks/auth-hooks.ts @@ -3,7 +3,7 @@ import { message } from 'antd'; import { useEffect, useMemo, useState } from 'react'; import { useNavigate, useSearchParams } from 'umi'; -export const useLoginWithGithub = () => { +export const useOAuthCallback = () => { const [currentQueryParameters, setSearchParams] = useSearchParams(); const error = currentQueryParameters.get('error'); const newQueryParameters: URLSearchParams = useMemo( @@ -12,26 +12,38 @@ export const useLoginWithGithub = () => { ); const navigate = useNavigate(); - if (error) { - message.error(error); - navigate('/login'); - newQueryParameters.delete('error'); - setSearchParams(newQueryParameters); - return; - } + useEffect(() => { + if (error) { + message.error(error); + setTimeout(() => { + navigate('/login'); + newQueryParameters.delete('error'); + setSearchParams(newQueryParameters); + }, 1000); + return; + } - const auth = currentQueryParameters.get('auth'); + const auth = currentQueryParameters.get('auth'); + if (auth) { + authorizationUtil.setAuthorization(auth); + newQueryParameters.delete('auth'); + setSearchParams(newQueryParameters); + navigate('/knowledge'); + } + }, [ + error, + currentQueryParameters, + newQueryParameters, + navigate, + setSearchParams, + ]); - if (auth) { - authorizationUtil.setAuthorization(auth); - newQueryParameters.delete('auth'); - setSearchParams(newQueryParameters); - } - return auth; + console.debug(currentQueryParameters.get('auth')); + return currentQueryParameters.get('auth'); }; export const useAuth = () => { - const auth = useLoginWithGithub(); + const auth = useOAuthCallback(); const [isLogin, setIsLogin] = useState>(null); useEffect(() => { diff --git a/web/src/hooks/login-hooks.ts b/web/src/hooks/login-hooks.ts index ddecc3ce7..b5fa0ae64 100644 --- a/web/src/hooks/login-hooks.ts +++ b/web/src/hooks/login-hooks.ts @@ -1,7 +1,10 @@ import { Authorization } from '@/constants/authorization'; -import userService from '@/services/user-service'; +import userService, { + getLoginChannels, + loginWithChannel, +} from '@/services/user-service'; import authorizationUtil, { redirectToLogin } from '@/utils/authorization-util'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { Form, message } from 'antd'; import { FormInstance } from 'antd/lib'; import { useEffect, useState } from 'react'; @@ -16,6 +19,36 @@ export interface IRegisterRequestBody extends ILoginRequestBody { nickname: string; } +export interface ILoginChannel { + channel: string; + display_name: string; + icon: string; +} + +export const useLoginChannels = () => { + const { data, isLoading } = useQuery({ + queryKey: ['loginChannels'], + queryFn: async () => { + const { data: res = {} } = await getLoginChannels(); + return res.data || []; + }, + }); + + return { channels: data as ILoginChannel[], loading: isLoading }; +}; + +export const useLoginWithChannel = () => { + const { isPending: loading, mutateAsync } = useMutation({ + mutationKey: ['loginWithChannel'], + mutationFn: async (channel: string) => { + loginWithChannel(channel); + return Promise.resolve(); + }, + }); + + return { loading, login: mutateAsync }; +}; + export const useLogin = () => { const { t } = useTranslation(); @@ -67,8 +100,13 @@ export const useRegister = () => { const { data = {} } = await userService.register(params); if (data.code === 0) { message.success(t('message.registered')); - } else if (data.message && data.message.includes('registration is disabled')) { - message.error(t('message.registerDisabled') || 'User registration is disabled'); + } else if ( + data.message && + data.message.includes('registration is disabled') + ) { + message.error( + t('message.registerDisabled') || 'User registration is disabled', + ); } return data.code; }, diff --git a/web/src/pages/login/index.less b/web/src/pages/login/index.less index 61c5d5443..78a3d2631 100644 --- a/web/src/pages/login/index.less +++ b/web/src/pages/login/index.less @@ -19,6 +19,35 @@ margin: 0 auto; } + .thirdPartyLoginButton { + margin-top: 10px; + border-top: 1px solid rgba(0, 0, 0, 0.06); + // padding-top: 0px; + + .ant-btn { + display: flex; + align-items: center; + justify-content: center; + height: 40px; + font-size: 14px; + border-radius: 4px; + border: 1px solid #d9d9d9; + background: #fff; + color: rgba(0, 0, 0, 0.85); + transition: all 0.3s; + + &:hover { + color: #40a9ff; + border-color: #40a9ff; + } + + .anticon { + font-size: 16px; + margin-right: 8px; + } + } + } + .loginRight { display: flex; align-items: center; diff --git a/web/src/pages/login/index.tsx b/web/src/pages/login/index.tsx index dab0b264c..19906164b 100644 --- a/web/src/pages/login/index.tsx +++ b/web/src/pages/login/index.tsx @@ -1,13 +1,19 @@ -import { useLogin, useRegister } from '@/hooks/login-hooks'; +import SvgIcon from '@/components/svg-icon'; +import { useAuth } from '@/hooks/auth-hooks'; +import { + useLogin, + useLoginChannels, + useLoginWithChannel, + useRegister, +} from '@/hooks/login-hooks'; import { useSystemConfig } from '@/hooks/system-hooks'; import { rsaPsw } from '@/utils'; import { Button, Checkbox, Form, Input } from 'antd'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Icon, useNavigate } from 'umi'; +import { useNavigate } from 'umi'; import RightPanel from './right-panel'; -import { Domain } from '@/constants/common'; import styles from './index.less'; const Login = () => { @@ -15,11 +21,29 @@ const Login = () => { const navigate = useNavigate(); const { login, loading: signLoading } = useLogin(); const { register, loading: registerLoading } = useRegister(); + const { channels, loading: channelsLoading } = useLoginChannels(); + const { login: loginWithChannel, loading: loginWithChannelLoading } = + useLoginWithChannel(); const { t } = useTranslation('translation', { keyPrefix: 'login' }); - const loading = signLoading || registerLoading; + const loading = + signLoading || + registerLoading || + channelsLoading || + loginWithChannelLoading; const { config } = useSystemConfig(); const registerEnabled = config?.registerEnabled !== 0; + const { isLogin } = useAuth(); + useEffect(() => { + if (isLogin) { + navigate('/knowledge'); + } + }, [isLogin, navigate]); + + const handleLoginWithChannel = async (channel: string) => { + await loginWithChannel(channel); + }; + const changeTitle = () => { if (title === 'login' && !registerEnabled) { return; @@ -65,11 +89,6 @@ const Login = () => { // wrapperCol: { span: 8 }, }; - const toGoogle = () => { - window.location.href = - 'https://github.com/login/oauth/authorize?scope=user:email&client_id=302129228f0d96055bee'; - }; - return (
@@ -151,39 +170,28 @@ const Login = () => { > {title === 'login' ? t('login') : t('continue')} - {title === 'login' && ( - <> - {/* */} - {location.host === Domain && ( + {title === 'login' && channels && channels.length > 0 && ( +
+ {channels.map((item) => ( - )} - + ))} +
)}
diff --git a/web/src/services/user-service.ts b/web/src/services/user-service.ts index d6f5f920a..1177f34af 100644 --- a/web/src/services/user-service.ts +++ b/web/src/services/user-service.ts @@ -123,6 +123,10 @@ const methods = { const userService = registerServer(methods, request); +export const getLoginChannels = () => request.get(api.login_channels); +export const loginWithChannel = (channel: string) => + (window.location.href = api.login_channel(channel)); + export const listTenantUser = (tenantId: string) => request.get(api.listTenantUser(tenantId)); diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index e9eff866a..e83e5109d 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -11,6 +11,8 @@ export default { user_info: `${api_host}/user/info`, tenant_info: `${api_host}/user/tenant_info`, set_tenant_info: `${api_host}/user/set_tenant_info`, + login_channels: `${api_host}/user/login/channels`, + login_channel: (channel: string) => `${api_host}/user/login/${channel}`, // team addTenantUser: (tenantId: string) => `${api_host}/tenant/${tenantId}/user`,