mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-06 02:30:47 -08:00
feat: allow user use Authing to get token
This commit is contained in:
parent
af32534288
commit
79847adbb3
5 changed files with 118 additions and 192 deletions
|
|
@ -42,6 +42,7 @@
|
|||
"ImportWiki": "导入WIKI",
|
||||
"CloneWiki": "克隆Wiki",
|
||||
"NotLoggedIn": "未登录",
|
||||
"LogoutToGetStorageServiceToken": "登录在线存储服务以获取凭证",
|
||||
"LogoutGithubAccount": "登出Github账号",
|
||||
"LoginGithubAccount": "登录Github账号",
|
||||
"GitToken": "Git Token",
|
||||
|
|
|
|||
111
src/components/TokenForm/GitTokenForm.tsx
Normal file
111
src/components/TokenForm/GitTokenForm.tsx
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import React, { useMemo, useCallback } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import AuthingSSO from '@authing/sso';
|
||||
|
||||
import { TextField, Button } from '@material-ui/core';
|
||||
|
||||
import { SupportedStorageServices, IAuthingUserInfo } from '@services/types';
|
||||
import { useUserInfoObservable } from '@services/auth/hooks';
|
||||
import { usePromiseValueAndSetter } from '@/helpers/useServiceValue';
|
||||
import GitHubLogin from './AuthingLoginButton';
|
||||
import { APP_ID, APP_DOMAIN } from '@/constants/auth';
|
||||
import { IUserInfos } from '@services/auth/interface';
|
||||
|
||||
const AuthingLoginButton = styled(Button)`
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
`;
|
||||
const GitTokenInput = styled(TextField)``;
|
||||
|
||||
export function GitTokenForm(props: {
|
||||
children?: JSX.Element | Array<JSX.Element | undefined | string>;
|
||||
storageService: SupportedStorageServices;
|
||||
}): JSX.Element {
|
||||
const { children, storageService } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
/**
|
||||
* Update tiddlywiki's editor user name when first time creating new workspace
|
||||
*/
|
||||
const [userName, userNameSetter] = usePromiseValueAndSetter(
|
||||
async () => await window.service.auth.get('userName'),
|
||||
async (newUserName) => await window.service.auth.set('userName', newUserName),
|
||||
);
|
||||
|
||||
const authing = useMemo(
|
||||
() =>
|
||||
new AuthingSSO({
|
||||
appId: APP_ID,
|
||||
appDomain: APP_DOMAIN,
|
||||
redirectUrl: 'http://localhost:3000',
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const onFailure = useCallback(async (error: Error) => {}, []);
|
||||
|
||||
const onClickLogin = useCallback(async () => {
|
||||
// clear token first, otherwise github login window won't give us a chance to see the form
|
||||
// void this.auth.logout();
|
||||
// window.remote.clearStorageData();
|
||||
try {
|
||||
await authing.login();
|
||||
|
||||
const { session, ...response } = await authing.trackSession();
|
||||
const isLogin = session !== null && session !== undefined;
|
||||
if (isLogin && 'userInfo' in response && response.userInfo?.thirdPartyIdentity?.accessToken !== undefined) {
|
||||
const accessTokenToSet = response?.userInfo?.thirdPartyIdentity?.accessToken;
|
||||
const authDataString = response?.userInfo?.oauth;
|
||||
if (accessTokenToSet !== undefined) {
|
||||
void window.service.auth.set((`${storageService}-token` as unknown) as keyof IUserInfos, accessTokenToSet);
|
||||
}
|
||||
// all data we need
|
||||
if (accessTokenToSet !== undefined && authDataString !== undefined) {
|
||||
const authData = JSON.parse(authDataString);
|
||||
const nextUserInfo: IAuthingUserInfo = {
|
||||
...response.userInfo,
|
||||
...authData,
|
||||
...response.userInfo?.thirdPartyIdentity,
|
||||
};
|
||||
delete nextUserInfo.oauth;
|
||||
delete nextUserInfo.thirdPartyIdentity;
|
||||
void window.service.auth.set((`${storageService}-userName` as unknown) as keyof IUserInfos, nextUserInfo.username);
|
||||
if (userName === undefined || userName === '') {
|
||||
userNameSetter(nextUserInfo.username);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
void onFailure(error);
|
||||
}
|
||||
}, [authing, onFailure]);
|
||||
const onClickLogout = useCallback(async () => {
|
||||
const { code, message } = await authing.logout();
|
||||
await window.service.window.clearStorageData();
|
||||
if (code === 200) {
|
||||
// TODO: clear the input
|
||||
} else {
|
||||
console.error(message);
|
||||
}
|
||||
}, [authing, onFailure]);
|
||||
|
||||
const userInfo = useUserInfoObservable();
|
||||
if (userInfo === undefined) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<AuthingLoginButton>{t('AddWorkspace.LogoutToGetStorageServiceToken')}</AuthingLoginButton>
|
||||
<GitTokenInput
|
||||
helperText={t('AddWorkspace.GitTokenDescription')}
|
||||
fullWidth
|
||||
onChange={(event) => {
|
||||
void window.service.auth.set(`${storageService}-token`, event.target.value);
|
||||
}}
|
||||
value={userInfo[`${storageService}-token`]}
|
||||
/>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -5,6 +5,9 @@ import { TabPanel, TabContext, TabList } from '@material-ui/lab';
|
|||
import { SupportedStorageServices } from '@services/types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import GitHubLogin from './AuthingLoginButton';
|
||||
import { GitTokenForm } from './GitTokenForm';
|
||||
|
||||
const Container = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
|
@ -47,7 +50,9 @@ export function TokenForm(): JSX.Element {
|
|||
<Tab label="GitLab" value={SupportedStorageServices.gitlab} />
|
||||
<Tab label="Gitee" value={SupportedStorageServices.gitee} />
|
||||
</TabList>
|
||||
<TabPanel value={SupportedStorageServices.github}>Item One</TabPanel>
|
||||
<TabPanel value={SupportedStorageServices.github}>
|
||||
<GitTokenForm storageService={SupportedStorageServices.github} />
|
||||
</TabPanel>
|
||||
<TabPanel value={SupportedStorageServices.gitlab}>Item Two</TabPanel>
|
||||
<TabPanel value={SupportedStorageServices.gitee}>Item Two</TabPanel>
|
||||
</TabsContainer>
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
|
||||
import GitHubLogin from './github-login';
|
||||
import type { IAuthingUserInfo } from '@services/types';
|
||||
import { useUserInfoObservable } from '@services/auth/hooks';
|
||||
|
||||
const GitTokenInput = styled(TextField)``;
|
||||
|
||||
export const setGithubToken = async (token: string | undefined): Promise<void> => await window.service.auth.set('github-token', token);
|
||||
export const getGithubToken = async (): Promise<string | undefined> => await window.service.auth.get('github-token');
|
||||
|
||||
export function GithubTokenForm(props: { children?: JSX.Element | Array<JSX.Element | undefined | string> }): JSX.Element {
|
||||
const { children } = props;
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
// on startup, loading the cachedGithubToken
|
||||
if (accessToken === undefined && cachedGithubToken !== undefined) {
|
||||
graphqlClient.setHeader('Authorization', `bearer ${cachedGithubToken}`);
|
||||
accessTokenSetter(cachedGithubToken);
|
||||
} else if (accessToken !== undefined && accessToken !== cachedGithubToken) {
|
||||
// if user or login button changed the token, we use latest token
|
||||
Object.keys(graphqlClient.headers).map((key) => graphqlClient.removeHeader(key));
|
||||
accessTokenSetter(accessToken);
|
||||
void setGithubToken(accessToken);
|
||||
}
|
||||
}, [cachedGithubToken, accessToken]);
|
||||
const userInfo = useUserInfoObservable();
|
||||
if (userInfo === undefined) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<GitHubLogin
|
||||
onSuccess={(response) => {
|
||||
const accessTokenToSet = response?.userInfo?.thirdPartyIdentity?.accessToken;
|
||||
const authDataString = response?.userInfo?.oauth;
|
||||
if (accessTokenToSet !== undefined) {
|
||||
void window.service.auth.set('github-token', accessTokenToSet);
|
||||
}
|
||||
// all data we need
|
||||
if (accessTokenToSet !== undefined && authDataString !== undefined) {
|
||||
const authData = JSON.parse(authDataString);
|
||||
const nextUserInfo = {
|
||||
...response.userInfo,
|
||||
...authData,
|
||||
...response.userInfo?.thirdPartyIdentity,
|
||||
};
|
||||
delete nextUserInfo.oauth;
|
||||
delete nextUserInfo.thirdPartyIdentity;
|
||||
void window.service.auth.set('github-userName', (nextUserInfo as IAuthingUserInfo).username);
|
||||
}
|
||||
}}
|
||||
onLogout={() => {
|
||||
void window.service.auth.set('github-token', '');
|
||||
void window.service.auth.set('github-userName', '');
|
||||
}}
|
||||
onFailure={() => {
|
||||
void window.service.auth.set('github-token', '');
|
||||
void window.service.auth.set('github-userName', '');
|
||||
}}
|
||||
/>
|
||||
<GitTokenInput
|
||||
helperText={t('AddWorkspace.GitTokenDescription')}
|
||||
fullWidth
|
||||
onChange={(event) => {
|
||||
void window.service.auth.set('github-token', event.target.value);
|
||||
}}
|
||||
value={userInfo['github-token']}
|
||||
/>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
/* eslint-disable promise/no-nesting */
|
||||
import React, { Component } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import GithubIcon from '@material-ui/icons/GitHub';
|
||||
import AuthingSSO, { ILoginInfo } from '@authing/sso';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
import { APP_DOMAIN, APP_ID } from '../../constants/auth';
|
||||
|
||||
const SyncToGithubButton = styled(Button)`
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
t: (x: string) => string;
|
||||
onRequest: () => unknown;
|
||||
onSuccess: (info: Partial<ILoginInfo>) => unknown;
|
||||
onLogout: () => unknown;
|
||||
onFailure: (error: Error) => unknown;
|
||||
}
|
||||
interface State {
|
||||
isLogin: boolean;
|
||||
}
|
||||
class GitHubLogin extends Component<Props, State> {
|
||||
static defaultProps: Partial<Props> = {
|
||||
onRequest: () => {},
|
||||
onSuccess: () => {},
|
||||
onLogout: () => {},
|
||||
onFailure: () => {},
|
||||
};
|
||||
|
||||
auth: AuthingSSO;
|
||||
intervalHandel?: NodeJS.Timeout;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isLogin: false,
|
||||
};
|
||||
this.auth = new AuthingSSO({
|
||||
appId: APP_ID,
|
||||
appDomain: APP_DOMAIN,
|
||||
redirectUrl: 'http://localhost:3000',
|
||||
});
|
||||
this.updateLoginState();
|
||||
}
|
||||
|
||||
async isLogin(): Promise<boolean> {
|
||||
const { onSuccess, onLogout } = this.props;
|
||||
const { session, ...rest } = await this.auth.trackSession();
|
||||
const isLogin = session !== null && session !== undefined;
|
||||
if (isLogin) {
|
||||
onSuccess(rest);
|
||||
} else {
|
||||
onLogout();
|
||||
}
|
||||
return isLogin;
|
||||
}
|
||||
|
||||
updateLoginState(): void {
|
||||
this.intervalHandel = setInterval(() => {
|
||||
void this.isLogin().then((isLogin) => {
|
||||
this.setState({ isLogin });
|
||||
if (this.intervalHandel !== undefined) {
|
||||
clearInterval(this.intervalHandel);
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const { onRequest, onLogout, onFailure, t } = this.props;
|
||||
const { isLogin } = this.state;
|
||||
return isLogin ? (
|
||||
<SyncToGithubButton
|
||||
onClick={async () => {
|
||||
const { code, message } = await this.auth.logout();
|
||||
await window.service.window.clearStorageData();
|
||||
if (code === 200) {
|
||||
this.setState({ isLogin: false });
|
||||
this.updateLoginState();
|
||||
onLogout();
|
||||
} else {
|
||||
console.error(message);
|
||||
}
|
||||
}}
|
||||
color="secondary"
|
||||
endIcon={<GithubIcon />}>
|
||||
{t('AddWorkspace.LogoutGithubAccount')}
|
||||
</SyncToGithubButton>
|
||||
) : (
|
||||
<SyncToGithubButton
|
||||
onClick={async () => {
|
||||
// clear token first, otherwise github login window won't give us a chance to see the form
|
||||
// void this.auth.logout();
|
||||
// window.remote.clearStorageData();
|
||||
try {
|
||||
onRequest();
|
||||
await this.auth.login();
|
||||
} catch (error) {
|
||||
onFailure(error);
|
||||
}
|
||||
}}
|
||||
color="secondary"
|
||||
endIcon={<GithubIcon />}>
|
||||
{t('AddWorkspace.LoginGithubAccount')}
|
||||
</SyncToGithubButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation()(GitHubLogin);
|
||||
Loading…
Add table
Add a link
Reference in a new issue