mirror of https://gitee.com/answerdev/answer.git
feat: add oauth plugin
This commit is contained in:
parent
a88b476803
commit
479892b156
|
@ -747,6 +747,13 @@ ui:
|
||||||
lang:
|
lang:
|
||||||
label: Interface Language
|
label: Interface Language
|
||||||
text: User interface language. It will change when you refresh the page.
|
text: User interface language. It will change when you refresh the page.
|
||||||
|
my_logins:
|
||||||
|
title: My Logins
|
||||||
|
lable: Log in or sign up on this site using these accounts.
|
||||||
|
modal_title: Remove Login
|
||||||
|
modal_content: Are you sure you want to remove this login from your account?
|
||||||
|
modal_confirm_btn: Remove
|
||||||
|
remove_success: Removed successfully
|
||||||
toast:
|
toast:
|
||||||
update: update success
|
update: update success
|
||||||
update_password: Password changed successfully.
|
update_password: Password changed successfully.
|
||||||
|
@ -1362,7 +1369,7 @@ ui:
|
||||||
btn_submit: Save
|
btn_submit: Save
|
||||||
not_found_props: "Required property {{ key }} not found."
|
not_found_props: "Required property {{ key }} not found."
|
||||||
select: Select
|
select: Select
|
||||||
|
|
||||||
page_review:
|
page_review:
|
||||||
review: Review
|
review: Review
|
||||||
proposed: proposed
|
proposed: proposed
|
||||||
|
|
|
@ -521,3 +521,14 @@ export interface OauthBindEmailReq {
|
||||||
email: string;
|
email: string;
|
||||||
must: boolean;
|
must: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OauthConnectorItem {
|
||||||
|
icon: string;
|
||||||
|
name: string;
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserOauthConnectorItem extends OauthConnectorItem {
|
||||||
|
binding: boolean;
|
||||||
|
external_id: string;
|
||||||
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { memo, FC } from 'react';
|
|
||||||
import { Button } from 'react-bootstrap';
|
|
||||||
|
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
import { Icon } from '@/components';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
// data: any[]; // should use oauth plugin schemes
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
const Index: FC<Props> = ({ className }) => {
|
|
||||||
return (
|
|
||||||
<div className={classnames('d-grid gap-2', className)}>
|
|
||||||
<Button
|
|
||||||
variant="outline-secondary"
|
|
||||||
href="https://github.com/login/oauth/authorize?client_id=8cb9d4760cfd71c24de9&edirect_uri=http://10.0.20.88:8080/answer/api/v1/connector/redirect/github">
|
|
||||||
<Icon name="github" className="me-2" />
|
|
||||||
<span>Connect with Github</span>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button variant="outline-secondary">
|
|
||||||
<Icon name="twitter" className="me-2" />
|
|
||||||
<span>Connect with Google</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(Index);
|
|
|
@ -32,7 +32,6 @@ import CustomizeTheme from './CustomizeTheme';
|
||||||
import PageTags from './PageTags';
|
import PageTags from './PageTags';
|
||||||
import QuestionListLoader from './QuestionListLoader';
|
import QuestionListLoader from './QuestionListLoader';
|
||||||
import TagsLoader from './TagsLoader';
|
import TagsLoader from './TagsLoader';
|
||||||
import OauthButtons from './OauthButtons';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Avatar,
|
Avatar,
|
||||||
|
@ -71,6 +70,5 @@ export {
|
||||||
PageTags,
|
PageTags,
|
||||||
QuestionListLoader,
|
QuestionListLoader,
|
||||||
TagsLoader,
|
TagsLoader,
|
||||||
OauthButtons,
|
|
||||||
};
|
};
|
||||||
export type { EditorRef, JSONSchema, UISchema };
|
export type { EditorRef, JSONSchema, UISchema };
|
||||||
|
|
|
@ -302,3 +302,9 @@ a {
|
||||||
.bg-fade-out {
|
.bg-fade-out {
|
||||||
animation: 2s ease 1s bg-fade-out;
|
animation: 2s ease 1s bg-fade-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btnSvg, .btnSvg:hover {
|
||||||
|
fill: currentColor;
|
||||||
|
vertical-align: -0.125em;
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ import type {
|
||||||
ImgCodeRes,
|
ImgCodeRes,
|
||||||
FormDataType,
|
FormDataType,
|
||||||
} from '@/common/interface';
|
} from '@/common/interface';
|
||||||
import { Unactivate, OauthButtons } from '@/components';
|
import { Unactivate } from '@/components';
|
||||||
|
import { PluginOauth } from '@/plugins';
|
||||||
import { loggedUserInfoStore, loginSettingStore } from '@/stores';
|
import { loggedUserInfoStore, loginSettingStore } from '@/stores';
|
||||||
import { guard, floppyNavigation, handleFormError } from '@/utils';
|
import { guard, floppyNavigation, handleFormError } from '@/utils';
|
||||||
import { login, checkImgCode } from '@/services';
|
import { login, checkImgCode } from '@/services';
|
||||||
|
@ -173,7 +174,7 @@ const Index: React.FC = () => {
|
||||||
<h3 className="text-center mb-5">{t('page_title')}</h3>
|
<h3 className="text-center mb-5">{t('page_title')}</h3>
|
||||||
{step === 1 && (
|
{step === 1 && (
|
||||||
<Col className="mx-auto" md={3}>
|
<Col className="mx-auto" md={3}>
|
||||||
<OauthButtons className="mb-5" />
|
<PluginOauth className="mb-5" />
|
||||||
<Form noValidate onSubmit={handleSubmit}>
|
<Form noValidate onSubmit={handleSubmit}>
|
||||||
<Form.Group controlId="email" className="mb-3">
|
<Form.Group controlId="email" className="mb-3">
|
||||||
<Form.Label>{t('email.label')}</Form.Label>
|
<Form.Label>{t('email.label')}</Form.Label>
|
||||||
|
|
|
@ -1,40 +1,65 @@
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { Button } from 'react-bootstrap';
|
import { Button } from 'react-bootstrap';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Icon, Modal } from '@/components';
|
import { Modal } from '@/components';
|
||||||
|
import { useOauthConnectorInfoByUser, userOauthUnbind } from '@/services';
|
||||||
|
import { useToast } from '@/hooks';
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
const deleteLogins = (type) => {
|
const { data, mutate } = useOauthConnectorInfoByUser();
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
const { t } = useTranslation('translation', {
|
||||||
|
keyPrefix: 'settings.my_logins',
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteLogins = (e, item) => {
|
||||||
|
if (!item.binding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: 'Remove Login',
|
title: t('modal_title'),
|
||||||
content: 'Are you sure you want to delete this logins?',
|
content: t('modal_content'),
|
||||||
confirmBtnVariant: 'danger',
|
confirmBtnVariant: 'danger',
|
||||||
confirmText: 'Remove',
|
confirmText: t('modal_confirm_btn'),
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
console.log('delete login by: ', type);
|
userOauthUnbind({ external_id: item.external_id }).then(() => {
|
||||||
|
mutate();
|
||||||
|
toast.onShow({
|
||||||
|
msg: t('remove_success'),
|
||||||
|
variant: 'success',
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="mt-5">
|
<div className="mt-5">
|
||||||
<div className="form-label">My Logins</div>
|
<div className="form-label">{t('title')}</div>
|
||||||
<small className="form-text mt-0">
|
<small className="form-text mt-0">{t('lable')}</small>
|
||||||
Log in or sign up on this site using these accounts.
|
|
||||||
</small>
|
|
||||||
|
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Button variant="outline-secondary" className="d-block mb-2">
|
{data?.map((item) => {
|
||||||
<Icon name="google" className="me-2" />
|
return (
|
||||||
<span>Connect with Google</span>
|
<Button
|
||||||
</Button>
|
variant={item.binding ? 'outline-danger' : 'outline-secondary'}
|
||||||
|
href={item.link}
|
||||||
<Button
|
onClick={(e) => deleteLogins(e, item)}
|
||||||
variant="outline-danger"
|
key={item.name}>
|
||||||
className="mb-2"
|
<svg
|
||||||
onClick={() => deleteLogins('github')}>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<Icon name="github" className="me-2" />
|
width="16"
|
||||||
<span>Remove GitHub</span>
|
height="16"
|
||||||
</Button>
|
className="btnSvg"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path d={item.icon} />
|
||||||
|
</svg>
|
||||||
|
<span> {item.name}</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { memo, FC } from 'react';
|
||||||
|
import { Button } from 'react-bootstrap';
|
||||||
|
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import { useGetStartUseOauthConnector } from '@/services';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
const Index: FC<Props> = ({ className }) => {
|
||||||
|
const { data } = useGetStartUseOauthConnector();
|
||||||
|
|
||||||
|
if (!data?.length) return null;
|
||||||
|
return (
|
||||||
|
<div className={classnames('d-grid gap-2', className)}>
|
||||||
|
{data?.map((item) => {
|
||||||
|
return (
|
||||||
|
<Button variant="outline-secondary" href={item.link} key={item.name}>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
className="btnSvg"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path d={item.icon} />
|
||||||
|
</svg>
|
||||||
|
<span> {item.name}</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(Index);
|
|
@ -0,0 +1,3 @@
|
||||||
|
import PluginOauth from './PluginOauth';
|
||||||
|
|
||||||
|
export { PluginOauth };
|
|
@ -1,6 +1,36 @@
|
||||||
|
import useSWR from 'swr';
|
||||||
|
|
||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
import type * as Type from '@/common/interface';
|
import type * as Type from '@/common/interface';
|
||||||
|
|
||||||
export const oAuthBindEmail = (data: Type.OauthBindEmailReq) => {
|
export const oAuthBindEmail = (data: Type.OauthBindEmailReq) => {
|
||||||
return request.post('/answer/api/v1/connector/binding/email', data);
|
return request.post('/answer/api/v1/connector/binding/email', data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useOauthConnectorInfoByUser = () => {
|
||||||
|
const { data, error, mutate } = useSWR<Type.UserOauthConnectorItem[]>(
|
||||||
|
'/answer/api/v1/connector/user/info',
|
||||||
|
request.instance.get,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
mutate,
|
||||||
|
isLoading: !data && !error,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const userOauthUnbind = (data: { external_id: string }) => {
|
||||||
|
return request.delete('/answer/api/v1/connector/user/unbinding', data);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetStartUseOauthConnector = () => {
|
||||||
|
const { data, error } = useSWR<Type.OauthConnectorItem[]>(
|
||||||
|
'/answer/api/v1/connector/info',
|
||||||
|
request.instance.get,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue