mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-22 03:33:06 +08:00
feat: support google login on desktop (#4053)
Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
@@ -35,7 +35,7 @@ export const AfterSignInSendEmail: FC<AuthPanelProps> = ({
|
|||||||
onClick={useCallback(() => {
|
onClick={useCallback(() => {
|
||||||
signInCloud('email', {
|
signInCloud('email', {
|
||||||
email,
|
email,
|
||||||
callbackUrl: buildCallbackUrl('signIn'),
|
callbackUrl: buildCallbackUrl('/auth/signIn'),
|
||||||
redirect: true,
|
redirect: true,
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
}, [email])}
|
}, [email])}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const AfterSignUpSendEmail: FC<AuthPanelProps> = ({
|
|||||||
onClick={useCallback(() => {
|
onClick={useCallback(() => {
|
||||||
signInCloud('email', {
|
signInCloud('email', {
|
||||||
email: email,
|
email: email,
|
||||||
callbackUrl: buildCallbackUrl('signUp'),
|
callbackUrl: buildCallbackUrl('/auth/signUp'),
|
||||||
redirect: true,
|
redirect: true,
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
}, [email])}
|
}, [email])}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { isDesktop } from '@affine/env/constant';
|
import { isDesktop } from '@affine/env/constant';
|
||||||
|
|
||||||
type Action = 'signUp' | 'changePassword' | 'signIn' | 'signUp';
|
export function buildCallbackUrl(callbackUrl: string) {
|
||||||
|
|
||||||
export function buildCallbackUrl(action: Action) {
|
|
||||||
const callbackUrl = `/auth/${action}`;
|
|
||||||
const params: string[][] = [];
|
const params: string[][] = [];
|
||||||
if (isDesktop && window.appInfo.schema) {
|
if (isDesktop && window.appInfo.schema) {
|
||||||
params.push(['schema', window.appInfo.schema]);
|
params.push(['schema', window.appInfo.schema]);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { AuthInput, ModalHeader } from '@affine/component/auth-components';
|
import { AuthInput, ModalHeader } from '@affine/component/auth-components';
|
||||||
import { pushNotificationAtom } from '@affine/component/notification-center';
|
import { pushNotificationAtom } from '@affine/component/notification-center';
|
||||||
import type { Notification } from '@affine/component/notification-center/index.jotai';
|
import type { Notification } from '@affine/component/notification-center/index.jotai';
|
||||||
|
import { isDesktop } from '@affine/env/constant';
|
||||||
import { getUserQuery } from '@affine/graphql';
|
import { getUserQuery } from '@affine/graphql';
|
||||||
import { Trans } from '@affine/i18n';
|
import { Trans } from '@affine/i18n';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
@@ -69,7 +70,7 @@ export const SignIn: FC<AuthPanelProps> = ({
|
|||||||
if (user) {
|
if (user) {
|
||||||
signInCloud('email', {
|
signInCloud('email', {
|
||||||
email: email,
|
email: email,
|
||||||
callbackUrl: buildCallbackUrl('signIn'),
|
callbackUrl: buildCallbackUrl('/auth/signIn'),
|
||||||
redirect: false,
|
redirect: false,
|
||||||
})
|
})
|
||||||
.then(res => handleSendEmailError(res, pushNotification))
|
.then(res => handleSendEmailError(res, pushNotification))
|
||||||
@@ -78,7 +79,7 @@ export const SignIn: FC<AuthPanelProps> = ({
|
|||||||
} else {
|
} else {
|
||||||
signInCloud('email', {
|
signInCloud('email', {
|
||||||
email: email,
|
email: email,
|
||||||
callbackUrl: buildCallbackUrl('signUp'),
|
callbackUrl: buildCallbackUrl('/auth/signUp'),
|
||||||
redirect: false,
|
redirect: false,
|
||||||
})
|
})
|
||||||
.then(res => handleSendEmailError(res, pushNotification))
|
.then(res => handleSendEmailError(res, pushNotification))
|
||||||
@@ -103,7 +104,16 @@ export const SignIn: FC<AuthPanelProps> = ({
|
|||||||
}}
|
}}
|
||||||
icon={<GoogleDuotoneIcon />}
|
icon={<GoogleDuotoneIcon />}
|
||||||
onClick={useCallback(() => {
|
onClick={useCallback(() => {
|
||||||
signInCloud('google').catch(console.error);
|
if (isDesktop) {
|
||||||
|
open(
|
||||||
|
`/desktop-signin?provider=google&callback_url=${buildCallbackUrl(
|
||||||
|
'/open-app/oauth-jwt'
|
||||||
|
)}`,
|
||||||
|
'_target'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
signInCloud('google').catch(console.error);
|
||||||
|
}
|
||||||
}, [])}
|
}, [])}
|
||||||
>
|
>
|
||||||
{t['Continue with Google']()}
|
{t['Continue with Google']()}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import type { LoaderFunction } from 'react-router-dom';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { signInCloud } from '../utils/cloud-utils';
|
||||||
|
|
||||||
|
const supportedProvider = z.enum(['google']);
|
||||||
|
|
||||||
|
export const loader: LoaderFunction = async ({ request }) => {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const searchParams = url.searchParams;
|
||||||
|
const provider = searchParams.get('provider');
|
||||||
|
const callback_url = searchParams.get('callback_url');
|
||||||
|
if (!callback_url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const maybeProvider = supportedProvider.safeParse(provider);
|
||||||
|
if (maybeProvider.success) {
|
||||||
|
const provider = maybeProvider.data;
|
||||||
|
await signInCloud(provider, {
|
||||||
|
callbackUrl: callback_url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Component = () => {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
|
import { type GetCurrentUserQuery, getCurrentUserQuery } from '@affine/graphql';
|
||||||
import { Trans } from '@affine/i18n';
|
import { Trans } from '@affine/i18n';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import { fetcher } from '@affine/workspace/affine/gql';
|
||||||
import { Logo1Icon } from '@blocksuite/icons';
|
import { Logo1Icon } from '@blocksuite/icons';
|
||||||
import { Button } from '@toeverything/components/button';
|
import { Button } from '@toeverything/components/button';
|
||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import {
|
||||||
|
type LoaderFunction,
|
||||||
|
useLoaderData,
|
||||||
|
useSearchParams,
|
||||||
|
} from 'react-router-dom';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import * as styles from './open-app.css';
|
import * as styles from './open-app.css';
|
||||||
@@ -45,24 +51,26 @@ const appNames = {
|
|||||||
internal: 'AFFiNE Internal',
|
internal: 'AFFiNE Internal',
|
||||||
} satisfies Record<Channel, string>;
|
} satisfies Record<Channel, string>;
|
||||||
|
|
||||||
export const Component = () => {
|
interface OpenAppProps {
|
||||||
|
urlToOpen?: string | null;
|
||||||
|
channel: Channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LoaderData {
|
||||||
|
action: 'url' | 'oauth-jwt';
|
||||||
|
currentUser?: GetCurrentUserQuery['currentUser'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const OpenAppImpl = ({ urlToOpen, channel }: OpenAppProps) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const [params] = useSearchParams();
|
|
||||||
const urlToOpen = useMemo(() => params.get('url'), [params]);
|
|
||||||
const autoOpen = useMemo(() => params.get('open') !== 'false', [params]);
|
|
||||||
const channel = useMemo(() => {
|
|
||||||
const urlObj = new URL(urlToOpen || '');
|
|
||||||
const maybeSchema = appSchemas.safeParse(urlObj.protocol.replace(':', ''));
|
|
||||||
return schemaToChanel[maybeSchema.success ? maybeSchema.data : 'affine'];
|
|
||||||
}, [urlToOpen]);
|
|
||||||
|
|
||||||
const appIcon = appIconMap[channel];
|
|
||||||
const appName = appNames[channel];
|
|
||||||
|
|
||||||
const openDownloadLink = useCallback(() => {
|
const openDownloadLink = useCallback(() => {
|
||||||
const url = `https://affine.pro/download?channel=${channel}`;
|
const url = `https://affine.pro/download?channel=${channel}`;
|
||||||
open(url, '_blank');
|
open(url, '_blank');
|
||||||
}, [channel]);
|
}, [channel]);
|
||||||
|
const appIcon = appIconMap[channel];
|
||||||
|
const appName = appNames[channel];
|
||||||
|
const [params] = useSearchParams();
|
||||||
|
const autoOpen = useMemo(() => params.get('open') !== 'false', [params]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!urlToOpen || lastOpened === urlToOpen || !autoOpen) {
|
if (!urlToOpen || lastOpened === urlToOpen || !autoOpen) {
|
||||||
@@ -72,80 +80,133 @@ export const Component = () => {
|
|||||||
open(urlToOpen, '_blank');
|
open(urlToOpen, '_blank');
|
||||||
}, [urlToOpen, autoOpen]);
|
}, [urlToOpen, autoOpen]);
|
||||||
|
|
||||||
if (urlToOpen) {
|
if (!urlToOpen) {
|
||||||
return (
|
return null;
|
||||||
<div className={styles.root}>
|
}
|
||||||
<div className={styles.topNav}>
|
|
||||||
|
return (
|
||||||
|
<div className={styles.root}>
|
||||||
|
<div className={styles.topNav}>
|
||||||
|
<a
|
||||||
|
href="https://affine.pro"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={styles.affineLogo}
|
||||||
|
>
|
||||||
|
<Logo1Icon width={24} height={24} />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div className={styles.topNavLinks}>
|
||||||
<a
|
<a
|
||||||
href="https://affine.pro"
|
href="https://affine.pro"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className={styles.affineLogo}
|
className={styles.topNavLink}
|
||||||
>
|
>
|
||||||
<Logo1Icon width={24} height={24} />
|
Official Website
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div className={styles.topNavLinks}>
|
|
||||||
<a
|
|
||||||
href="https://affine.pro"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className={styles.topNavLink}
|
|
||||||
>
|
|
||||||
Official Website
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://community.affine.pro/home"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className={styles.topNavLink}
|
|
||||||
>
|
|
||||||
AFFiNE Community
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://affine.pro/blog"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className={styles.topNavLink}
|
|
||||||
>
|
|
||||||
Blog
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://affine.pro/about-us"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className={styles.topNavLink}
|
|
||||||
>
|
|
||||||
Contact us
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button onClick={openDownloadLink}>
|
|
||||||
{t['com.affine.auth.open.affine.download-app']()}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.centerContent}>
|
|
||||||
<img src={appIcon} alt={appName} width={120} height={120} />
|
|
||||||
|
|
||||||
<div className={styles.prompt}>
|
|
||||||
<Trans i18nKey="com.affine.auth.open.affine.prompt">
|
|
||||||
Open {appName} app now
|
|
||||||
</Trans>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a
|
<a
|
||||||
className={styles.tryAgainLink}
|
href="https://community.affine.pro/home"
|
||||||
href={urlToOpen}
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
className={styles.topNavLink}
|
||||||
>
|
>
|
||||||
{t['com.affine.auth.open.affine.try-again']()}
|
AFFiNE Community
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://affine.pro/blog"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={styles.topNavLink}
|
||||||
|
>
|
||||||
|
Blog
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://affine.pro/about-us"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={styles.topNavLink}
|
||||||
|
>
|
||||||
|
Contact us
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Button onClick={openDownloadLink}>
|
||||||
|
{t['com.affine.auth.open.affine.download-app']()}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
} else {
|
<div className={styles.centerContent}>
|
||||||
|
<img src={appIcon} alt={appName} width={120} height={120} />
|
||||||
|
|
||||||
|
<div className={styles.prompt}>
|
||||||
|
<Trans i18nKey="com.affine.auth.open.affine.prompt">
|
||||||
|
Open {appName} app now
|
||||||
|
</Trans>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
className={styles.tryAgainLink}
|
||||||
|
href={urlToOpen}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{t['com.affine.auth.open.affine.try-again']()}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const OpenUrl = () => {
|
||||||
|
const [params] = useSearchParams();
|
||||||
|
const urlToOpen = useMemo(() => params.get('url'), [params]);
|
||||||
|
const channel = useMemo(() => {
|
||||||
|
const urlObj = new URL(urlToOpen || '');
|
||||||
|
const maybeSchema = appSchemas.safeParse(urlObj.protocol.replace(':', ''));
|
||||||
|
return schemaToChanel[maybeSchema.success ? maybeSchema.data : 'affine'];
|
||||||
|
}, [urlToOpen]);
|
||||||
|
|
||||||
|
return <OpenAppImpl urlToOpen={urlToOpen} channel={channel} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const OpenOAuthJwt = () => {
|
||||||
|
const { currentUser } = useLoaderData() as LoaderData;
|
||||||
|
const [params] = useSearchParams();
|
||||||
|
const schema = useMemo(() => {
|
||||||
|
const maybeSchema = appSchemas.safeParse(params.get('schema'));
|
||||||
|
return maybeSchema.success ? maybeSchema.data : 'affine';
|
||||||
|
}, [params]);
|
||||||
|
const channel = schemaToChanel[schema as Schema];
|
||||||
|
|
||||||
|
if (!currentUser || !currentUser?.token?.token) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const urlToOpen = `${schema}://oauth-jwt?token=${currentUser.token.token}`;
|
||||||
|
|
||||||
|
return <OpenAppImpl urlToOpen={urlToOpen} channel={channel} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Component = () => {
|
||||||
|
const { action } = useLoaderData() as LoaderData;
|
||||||
|
|
||||||
|
if (action === 'url') {
|
||||||
|
return <OpenUrl />;
|
||||||
|
} else if (action === 'oauth-jwt') {
|
||||||
|
return <OpenOAuthJwt />;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loader: LoaderFunction = async args => {
|
||||||
|
const action = args.params.action || '';
|
||||||
|
const res = await fetcher({
|
||||||
|
query: getCurrentUserQuery,
|
||||||
|
}).catch(console.error);
|
||||||
|
|
||||||
|
return {
|
||||||
|
action,
|
||||||
|
currentUser: res?.currentUser || null,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,9 +49,13 @@ export const routes = [
|
|||||||
lazy: () => import('./pages/sign-in'),
|
lazy: () => import('./pages/sign-in'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/open-app',
|
path: '/open-app/:action',
|
||||||
lazy: () => import('./pages/open-app'),
|
lazy: () => import('./pages/open-app'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/desktop-signin',
|
||||||
|
lazy: () => import('./pages/desktop-signin'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '*',
|
path: '*',
|
||||||
lazy: () => import('./pages/404'),
|
lazy: () => import('./pages/404'),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { logger } from './logger';
|
|||||||
import {
|
import {
|
||||||
handleOpenUrlInHiddenWindow,
|
handleOpenUrlInHiddenWindow,
|
||||||
restoreOrCreateWindow,
|
restoreOrCreateWindow,
|
||||||
|
setCookie,
|
||||||
} from './main-window';
|
} from './main-window';
|
||||||
import { uiSubjects } from './ui';
|
import { uiSubjects } from './ui';
|
||||||
|
|
||||||
@@ -62,6 +63,8 @@ async function handleAffineUrl(url: string) {
|
|||||||
if (urlToOpen) {
|
if (urlToOpen) {
|
||||||
await handleSignIn(urlToOpen);
|
await handleSignIn(urlToOpen);
|
||||||
}
|
}
|
||||||
|
} else if (urlObj.hostname === 'oauth-jwt') {
|
||||||
|
await handleOauthJwt(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,3 +106,32 @@ async function handleSignIn(url: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleOauthJwt(url: string) {
|
||||||
|
if (url) {
|
||||||
|
try {
|
||||||
|
const mainWindow = await restoreOrCreateWindow();
|
||||||
|
mainWindow.show();
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
const token = urlObj.searchParams.get('token');
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
logger.error('no token in url', url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set token to cookie
|
||||||
|
await setCookie({
|
||||||
|
url: new URL(mainWindow.webContents.getURL()).origin,
|
||||||
|
httpOnly: true,
|
||||||
|
value: token,
|
||||||
|
name: 'next-auth.session-token',
|
||||||
|
});
|
||||||
|
|
||||||
|
// force reload app
|
||||||
|
mainWindow.webContents.reload();
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('failed to open url in popup', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import assert from 'node:assert';
|
import assert from 'node:assert';
|
||||||
|
|
||||||
import { BrowserWindow, nativeTheme } from 'electron';
|
import { BrowserWindow, type CookiesSetDetails, nativeTheme } from 'electron';
|
||||||
import electronWindowState from 'electron-window-state';
|
import electronWindowState from 'electron-window-state';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
@@ -174,7 +174,22 @@ export function reloadApp() {
|
|||||||
browserWindow?.reload();
|
browserWindow?.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setCookie(origin: string, cookie: string) {
|
export async function setCookie(cookie: CookiesSetDetails): Promise<void>;
|
||||||
|
export async function setCookie(origin: string, cookie: string): Promise<void>;
|
||||||
|
|
||||||
|
export async function setCookie(
|
||||||
|
arg0: CookiesSetDetails | string,
|
||||||
|
arg1?: string
|
||||||
|
) {
|
||||||
const window = await restoreOrCreateWindow();
|
const window = await restoreOrCreateWindow();
|
||||||
await window.webContents.session.cookies.set(parseCookie(cookie, origin));
|
const details =
|
||||||
|
typeof arg1 === 'string' && typeof arg0 === 'string'
|
||||||
|
? parseCookie(arg0, arg1)
|
||||||
|
: arg0;
|
||||||
|
|
||||||
|
if (typeof details !== 'object') {
|
||||||
|
throw new Error('invalid cookie details');
|
||||||
|
}
|
||||||
|
|
||||||
|
await window.webContents.session.cookies.set(details);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class AuthResolver {
|
|||||||
|
|
||||||
@ResolveField(() => TokenType)
|
@ResolveField(() => TokenType)
|
||||||
token(@CurrentUser() currentUser: UserType, @Parent() user: UserType) {
|
token(@CurrentUser() currentUser: UserType, @Parent() user: UserType) {
|
||||||
if (user !== currentUser) {
|
if (user.id !== currentUser.id) {
|
||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,14 +83,20 @@ export class UserResolver {
|
|||||||
description: 'Get current user',
|
description: 'Get current user',
|
||||||
})
|
})
|
||||||
async currentUser(@CurrentUser() user: User) {
|
async currentUser(@CurrentUser() user: User) {
|
||||||
|
const storedUser = await this.prisma.user.findUnique({
|
||||||
|
where: { id: user.id },
|
||||||
|
});
|
||||||
|
if (!storedUser) {
|
||||||
|
throw new BadRequestException(`User ${user.id} not found in db`);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
id: user.id,
|
id: storedUser.id,
|
||||||
name: user.name,
|
name: storedUser.name,
|
||||||
email: user.email,
|
email: storedUser.email,
|
||||||
emailVerified: user.emailVerified,
|
emailVerified: storedUser.emailVerified,
|
||||||
avatarUrl: user.avatarUrl,
|
avatarUrl: storedUser.avatarUrl,
|
||||||
createdAt: user.createdAt,
|
createdAt: storedUser.createdAt,
|
||||||
hasPassword: !!user.password,
|
hasPassword: !!storedUser.password,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,5 +6,8 @@ query getCurrentUser {
|
|||||||
emailVerified
|
emailVerified
|
||||||
avatarUrl
|
avatarUrl
|
||||||
createdAt
|
createdAt
|
||||||
|
token {
|
||||||
|
token
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ query getCurrentUser {
|
|||||||
emailVerified
|
emailVerified
|
||||||
avatarUrl
|
avatarUrl
|
||||||
createdAt
|
createdAt
|
||||||
|
token {
|
||||||
|
token
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ export type GetCurrentUserQuery = {
|
|||||||
emailVerified: string | null;
|
emailVerified: string | null;
|
||||||
avatarUrl: string | null;
|
avatarUrl: string | null;
|
||||||
createdAt: string | null;
|
createdAt: string | null;
|
||||||
|
token: { __typename?: 'TokenType'; token: string };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user