From c0669359edb8511b8a758667856ba2df3d45a249 Mon Sep 17 00:00:00 2001 From: Horus Date: Wed, 12 Apr 2023 02:42:36 +0800 Subject: [PATCH] feat: support google cloud login in client (#1822) Co-authored-by: Himself65 Co-authored-by: Peng Xiao --- .github/workflows/release-desktop-app.yml | 3 ++ apps/electron/.gitignore | 1 + .../layers/main/src/app-state/google-auth.ts | 32 ++++++++++++ .../layers/main/src/app-state/index.ts | 40 +++++++++++++++ apps/electron/layers/main/src/index.ts | 19 ++++++- .../layers/main/src/security-restrictions.ts | 8 +++ apps/electron/layers/preload/preload.d.ts | 2 +- apps/electron/layers/preload/src/index.ts | 13 ++++- apps/electron/package.json | 15 +++++- apps/electron/scripts/common.mjs | 49 ++++++++++++------- apps/electron/scripts/dev.mjs | 34 ++++++++++--- apps/electron/scripts/generate-assets.mjs | 10 ++-- apps/electron/yarn.lock | 39 ++++++++++++++- .../components/blocksuite/header/styles.ts | 1 - apps/web/src/shared/apis.ts | 5 +- packages/workspace/src/affine/login.ts | 21 ++++++-- packages/workspace/src/type.ts | 2 + 17 files changed, 252 insertions(+), 42 deletions(-) create mode 100644 apps/electron/layers/main/src/app-state/google-auth.ts diff --git a/.github/workflows/release-desktop-app.yml b/.github/workflows/release-desktop-app.yml index c3ec1d1b96..02307e2b63 100644 --- a/.github/workflows/release-desktop-app.yml +++ b/.github/workflows/release-desktop-app.yml @@ -40,6 +40,9 @@ env: NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID }} NEXT_PUBLIC_FIREBASE_APP_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_APP_ID }} NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID }} + AFFINE_GOOGLE_CLIENT_ID: ${{ secrets.AFFINE_GOOGLE_CLIENT_ID }} + AFFINE_GOOGLE_CLIENT_SECRET: ${{ secrets.AFFINE_GOOGLE_CLIENT_SECRET }} + NODE_API_SERVER: 'https://app.affine.pro' jobs: make-macos: diff --git a/apps/electron/.gitignore b/apps/electron/.gitignore index 96f20985f5..ca28910495 100644 --- a/apps/electron/.gitignore +++ b/apps/electron/.gitignore @@ -11,3 +11,4 @@ resources/web-static !.yarn/releases !.yarn/sdks !.yarn/versions +dev.json diff --git a/apps/electron/layers/main/src/app-state/google-auth.ts b/apps/electron/layers/main/src/app-state/google-auth.ts new file mode 100644 index 0000000000..c04fcb3a23 --- /dev/null +++ b/apps/electron/layers/main/src/app-state/google-auth.ts @@ -0,0 +1,32 @@ +import type { RequestInit } from 'undici'; +import { fetch, ProxyAgent } from 'undici'; + +const redirectUri = 'https://affine.pro/client/auth-callback'; + +export const oauthEndpoint = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${process.env.AFFINE_GOOGLE_CLIENT_ID}&redirect_uri=${redirectUri}&response_type=code&scope=openid https://www.googleapis.com/auth/userinfo.email profile&access_type=offline`; + +const tokenEndpoint = 'https://oauth2.googleapis.com/token'; + +export const exchangeToken = async (code: string) => { + const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy; + const proxyAgent = httpProxy ? new ProxyAgent(httpProxy) : undefined; + + const postData = { + code, + client_id: process.env.AFFINE_GOOGLE_CLIENT_ID || '', + client_secret: process.env.AFFINE_GOOGLE_CLIENT_SECRET || '', + redirect_uri: redirectUri, + grant_type: 'authorization_code', + }; + const requestOptions: RequestInit = { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams(postData).toString(), + dispatcher: proxyAgent, + }; + return fetch(tokenEndpoint, requestOptions).then(response => { + return response.json(); + }); +}; diff --git a/apps/electron/layers/main/src/app-state/index.ts b/apps/electron/layers/main/src/app-state/index.ts index 4fda185f6a..cb0c0e681c 100644 --- a/apps/electron/layers/main/src/app-state/index.ts +++ b/apps/electron/layers/main/src/app-state/index.ts @@ -2,13 +2,19 @@ import * as os from 'node:os'; import path from 'node:path'; import { Storage } from '@affine/octobase-node'; +import { app, shell } from 'electron'; import { BrowserWindow, ipcMain, nativeTheme } from 'electron'; import fs from 'fs-extra'; +import { parse } from 'url'; + +import { exchangeToken, oauthEndpoint } from './google-auth'; const AFFINE_ROOT = path.join(os.homedir(), '.affine'); fs.ensureDirSync(AFFINE_ROOT); +const logger = console; + // todo: rethink this export const appState = { storage: new Storage(path.join(AFFINE_ROOT, 'test.db')), @@ -21,6 +27,7 @@ export const registerHandlers = () => { ipcMain.handle('ui:theme-change', async (_, theme) => { nativeTheme.themeSource = theme; + logger.info('theme change', theme); }); ipcMain.handle('ui:sidebar-visibility-change', async (_, visible) => { @@ -30,5 +37,38 @@ export const registerHandlers = () => { // hide window buttons when sidebar is not visible w.setWindowButtonVisibility(visible); }); + logger.info('sidebar visibility change', visible); + }); + + ipcMain.handle('ui:google-sign-in', async () => { + logger.info('starting google sign in ...'); + shell.openExternal(oauthEndpoint); + + return new Promise((resolve, reject) => { + const handleOpenUrl = async (_: any, url: string) => { + const mainWindow = BrowserWindow.getAllWindows().find( + w => !w.isDestroyed() + ); + const urlObj = parse(url.replace('??', '?'), true); + if (!mainWindow || !url.startsWith('affine://')) return; + const token = (await exchangeToken(urlObj.query['code'] as string)) as { + id_token: string; + }; + app.removeListener('open-url', handleOpenUrl); + resolve(token.id_token); + logger.info('google sign in', token); + }; + + app.on('open-url', handleOpenUrl); + + setTimeout(() => { + reject(new Error('Timed out')); + app.removeListener('open-url', handleOpenUrl); + }, 60000); + }); + }); + + ipcMain.handle('main:env-update', async (_, env, value) => { + process.env[env] = value; }); }; diff --git a/apps/electron/layers/main/src/index.ts b/apps/electron/layers/main/src/index.ts index f2cd54efe6..4f7b7ba552 100644 --- a/apps/electron/layers/main/src/index.ts +++ b/apps/electron/layers/main/src/index.ts @@ -1,11 +1,21 @@ import './security-restrictions'; import { app } from 'electron'; +import path from 'path'; import { registerHandlers } from './app-state'; import { restoreOrCreateWindow } from './main-window'; import { registerProtocol } from './protocol'; +if (process.defaultApp) { + if (process.argv.length >= 2) { + app.setAsDefaultProtocolClient('affine', process.execPath, [ + path.resolve(process.argv[1]), + ]); + } +} else { + app.setAsDefaultProtocolClient('affine'); +} /** * Prevent multiple instances */ @@ -15,7 +25,13 @@ if (!isSingleInstance) { process.exit(0); } -app.on('second-instance', restoreOrCreateWindow); +app.on('second-instance', (event, argv) => { + restoreOrCreateWindow(); +}); + +app.on('open-url', async (_, url) => { + // todo: handle `affine://...` urls +}); /** * Disable Hardware Acceleration for more power-save @@ -45,7 +61,6 @@ app .then(registerHandlers) .then(restoreOrCreateWindow) .catch(e => console.error('Failed create window:', e)); - /** * Check new app version in production mode only */ diff --git a/apps/electron/layers/main/src/security-restrictions.ts b/apps/electron/layers/main/src/security-restrictions.ts index f826e7ea01..6596ee2656 100644 --- a/apps/electron/layers/main/src/security-restrictions.ts +++ b/apps/electron/layers/main/src/security-restrictions.ts @@ -10,6 +10,14 @@ app.on('web-contents-created', (_, contents) => { * @see https://www.electronjs.org/docs/latest/tutorial/security#13-disable-or-limit-navigation */ contents.on('will-navigate', (event, url) => { + if ( + (process.env.DEV_SERVER_URL && + url.startsWith(process.env.DEV_SERVER_URL)) || + url.startsWith('affine://') || + url.startsWith('file://.') + ) { + return; + } // Prevent navigation event.preventDefault(); shell.openExternal(url).catch(console.error); diff --git a/apps/electron/layers/preload/preload.d.ts b/apps/electron/layers/preload/preload.d.ts index 578259644e..d407bda96b 100644 --- a/apps/electron/layers/preload/preload.d.ts +++ b/apps/electron/layers/preload/preload.d.ts @@ -7,6 +7,6 @@ interface Window { * * @see https://github.com/cawa-93/dts-for-context-bridge */ - readonly apis: { workspaceSync: (id: string) => Promise; onThemeChange: (theme: string) => Promise; onSidebarVisibilityChange: (visible: boolean) => Promise; }; + readonly apis: { workspaceSync: (id: string) => Promise; onThemeChange: (theme: string) => Promise; onSidebarVisibilityChange: (visible: boolean) => Promise; googleSignIn: () => Promise; updateEnv: (env: string, value: string) => void; }; readonly appInfo: { electron: boolean; isMacOS: boolean; }; } diff --git a/apps/electron/layers/preload/src/index.ts b/apps/electron/layers/preload/src/index.ts index db47d29eec..ab900821a9 100644 --- a/apps/electron/layers/preload/src/index.ts +++ b/apps/electron/layers/preload/src/index.ts @@ -21,7 +21,6 @@ import { isMacOS } from '../../utils'; * * @see https://github.com/cawa-93/dts-for-context-bridge */ - contextBridge.exposeInMainWorld('apis', { workspaceSync: (id: string) => ipcRenderer.invoke('octo:workspace-sync', id), // ui @@ -30,6 +29,18 @@ contextBridge.exposeInMainWorld('apis', { onSidebarVisibilityChange: (visible: boolean) => ipcRenderer.invoke('ui:sidebar-visibility-change', visible), + + /** + * Try sign in using Google and return a Google IDToken + */ + googleSignIn: (): Promise => ipcRenderer.invoke('ui:google-sign-in'), + + /** + * Secret backdoor to update environment variables in main process + */ + updateEnv: (env: string, value: string) => { + ipcRenderer.invoke('main:env-update', env, value); + }, }); contextBridge.exposeInMainWorld('appInfo', { diff --git a/apps/electron/package.json b/apps/electron/package.json index d8bc9f47e1..ed81b54a95 100644 --- a/apps/electron/package.json +++ b/apps/electron/package.json @@ -36,6 +36,7 @@ "@electron-forge/maker-zip": "^6.1.1", "@electron-forge/shared-types": "^6.1.1", "@electron/rebuild": "^3.2.10", + "@electron/remote": "2.0.9", "dts-for-context-bridge": "^0.7.1", "electron": "24.0.0", "esbuild": "^0.17.16", @@ -44,7 +45,19 @@ "dependencies": { "cross-env": "7.0.3", "electron-window-state": "^5.0.3", - "fs-extra": "^11.1.1" + "firebase": "^9.18.0", + "fs-extra": "^11.1.1", + "undici": "^5.21.2" + }, + "build": { + "protocols": [ + { + "name": "affine", + "schemes": [ + "affine" + ] + } + ] }, "packageManager": "yarn@3.5.0" } diff --git a/apps/electron/scripts/common.mjs b/apps/electron/scripts/common.mjs index c46b1fc363..eeab85a9c1 100644 --- a/apps/electron/scripts/common.mjs +++ b/apps/electron/scripts/common.mjs @@ -2,7 +2,6 @@ import fs from 'node:fs'; import path from 'node:path'; import * as url from 'node:url'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); -// const __dirname = new URL('.', import.meta.url).pathname; const { node } = JSON.parse( fs.readFileSync( path.join(__dirname, '../electron-vendors.autogen.json'), @@ -20,22 +19,36 @@ const nativeNodeModulesPlugin = { }, }; -/** @type {import('esbuild').BuildOptions} */ -export const mainConfig = { - entryPoints: ['layers/main/src/index.ts'], - outdir: 'dist/layers/main', - bundle: true, - target: `node${node}`, - platform: 'node', - external: ['electron'], - plugins: [nativeNodeModulesPlugin], -}; +// List of env that will be replaced by esbuild +const ENV_MACROS = ['AFFINE_GOOGLE_CLIENT_ID', 'AFFINE_GOOGLE_CLIENT_SECRET']; -export const preloadConfig = { - entryPoints: ['layers/preload/src/index.ts'], - outdir: 'dist/layers/preload', - bundle: true, - target: `node${node}`, - platform: 'node', - external: ['electron'], +/** @return {{main: import('esbuild').BuildOptions, preload: import('esbuild').BuildOptions}} */ +export default () => { + const define = Object.fromEntries( + ENV_MACROS.map(key => [ + 'process.env.' + key, + JSON.stringify(process.env[key] ?? ''), + ]) + ); + return { + main: { + entryPoints: ['layers/main/src/index.ts'], + outdir: 'dist/layers/main', + bundle: true, + target: `node${node}`, + platform: 'node', + external: ['electron'], + plugins: [nativeNodeModulesPlugin], + define: define, + }, + preload: { + entryPoints: ['layers/preload/src/index.ts'], + outdir: 'dist/layers/preload', + bundle: true, + target: `node${node}`, + platform: 'node', + external: ['electron'], + define: define, + }, + }; }; diff --git a/apps/electron/scripts/dev.mjs b/apps/electron/scripts/dev.mjs index 34602218ef..7ec8b75beb 100644 --- a/apps/electron/scripts/dev.mjs +++ b/apps/electron/scripts/dev.mjs @@ -1,10 +1,16 @@ import { spawn } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { generateAsync } from 'dts-for-context-bridge'; import electronPath from 'electron'; import * as esbuild from 'esbuild'; -import { mainConfig, preloadConfig } from './common.mjs'; +import commonFn from './common.mjs'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); /** @type 'production' | 'development'' */ const mode = (process.env.NODE_ENV = process.env.NODE_ENV || 'development'); @@ -17,6 +23,17 @@ const stderrFilterPatterns = [ /ExtensionLoadWarning/, ]; +// these are set before calling commonFn so we have a chance to override them +try { + const devJson = readFileSync(path.resolve(__dirname, '../dev.json'), 'utf-8'); + const devEnv = JSON.parse(devJson); + Object.assign(process.env, devEnv); +} catch (err) { + console.warn( + `Could not read dev.json. Some functions may not work as expected.` + ); +} + // hard-coded for now: // fixme(xp): report error if app is not running on port 8080 process.env.DEV_SERVER_URL = `http://localhost:8080`; @@ -35,26 +52,28 @@ function spawnOrReloadElectron() { spawnProcess.stdout.on( 'data', - d => d.toString().trim() && console.warn(d.toString(), { timestamp: true }) + d => d.toString().trim() && console.warn(d.toString()) ); spawnProcess.stderr.on('data', d => { const data = d.toString().trim(); if (!data) return; const mayIgnore = stderrFilterPatterns.some(r => r.test(data)); if (mayIgnore) return; - console.error(data, { timestamp: true }); + console.error(data); }); // Stops the watch script when the application has been quit spawnProcess.on('exit', process.exit); } +const common = commonFn(); + async function main() { async function watchPreload(onInitialBuild) { const preloadBuild = await esbuild.context({ - ...preloadConfig, + ...common.preload, plugins: [ - ...(preloadConfig.plugins ?? []), + ...(common.preload.plugins ?? []), { name: 'affine-dev:reload-app-on-preload-change', setup(build) { @@ -81,13 +100,14 @@ async function main() { async function watchMain() { const mainBuild = await esbuild.context({ - ...mainConfig, + ...common.main, define: { + ...common.main.define, 'process.env.NODE_ENV': `"${mode}"`, 'process.env.DEV_SERVER_URL': `"${process.env.DEV_SERVER_URL}"`, }, plugins: [ - ...(mainConfig.plugins ?? []), + ...(common.main.plugins ?? []), { name: 'affine-dev:reload-app-on-main-change', setup(build) { diff --git a/apps/electron/scripts/generate-assets.mjs b/apps/electron/scripts/generate-assets.mjs index 1111506040..fc7d04f0c1 100644 --- a/apps/electron/scripts/generate-assets.mjs +++ b/apps/electron/scripts/generate-assets.mjs @@ -5,7 +5,7 @@ import path from 'node:path'; import * as esbuild from 'esbuild'; -import { mainConfig, preloadConfig } from './common.mjs'; +import commonFn from './common.mjs'; const repoRootDir = path.join(__dirname, '..', '..', '..'); const electronRootDir = path.join(__dirname, '..'); @@ -62,13 +62,13 @@ async function cleanup() { } async function buildLayers() { - await esbuild.build({ - ...preloadConfig, - }); + const common = commonFn(); + await esbuild.build(common.preload); await esbuild.build({ - ...mainConfig, + ...common.main, define: { + ...common.main.define, 'process.env.NODE_ENV': `"production"`, }, }); diff --git a/apps/electron/yarn.lock b/apps/electron/yarn.lock index 64f75cfe12..c637b94186 100644 --- a/apps/electron/yarn.lock +++ b/apps/electron/yarn.lock @@ -123,12 +123,15 @@ __metadata: "@electron-forge/maker-zip": ^6.1.1 "@electron-forge/shared-types": ^6.1.1 "@electron/rebuild": ^3.2.10 + "@electron/remote": 2.0.9 cross-env: 7.0.3 dts-for-context-bridge: ^0.7.1 electron: 24.0.0 electron-window-state: ^5.0.3 esbuild: ^0.17.16 + firebase: ^9.18.0 fs-extra: ^11.1.1 + undici: ^5.21.2 zx: ^7.2.1 languageName: unknown linkType: soft @@ -2234,6 +2237,15 @@ __metadata: languageName: node linkType: hard +"@electron/remote@npm:2.0.9": + version: 2.0.9 + resolution: "@electron/remote@npm:2.0.9" + peerDependencies: + electron: ">= 13.0.0" + checksum: 7949c528df0ecc9661c0b43e7f2586befb9eddfb53739adf204dc2097ba4331fb08b01f6904636e4c566d28b051a80a55718fb643ee2d2df0793c0c05278dbd4 + languageName: node + linkType: hard + "@electron/universal@npm:^1.3.2": version: 1.3.4 resolution: "@electron/universal@npm:1.3.4" @@ -7268,6 +7280,15 @@ __metadata: languageName: node linkType: hard +"busboy@npm:^1.6.0": + version: 1.6.0 + resolution: "busboy@npm:1.6.0" + dependencies: + streamsearch: ^1.1.0 + checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e + languageName: node + linkType: hard + "bytes@npm:3.0.0": version: 3.0.0 resolution: "bytes@npm:3.0.0" @@ -9636,7 +9657,7 @@ __metadata: languageName: node linkType: hard -"firebase@npm:^9.19.1": +"firebase@npm:^9.18.0, firebase@npm:^9.19.1": version: 9.19.1 resolution: "firebase@npm:9.19.1" dependencies: @@ -15960,6 +15981,13 @@ __metadata: languageName: node linkType: hard +"streamsearch@npm:^1.1.0": + version: 1.1.0 + resolution: "streamsearch@npm:1.1.0" + checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942 + languageName: node + linkType: hard + "string-argv@npm:~0.3.1": version: 0.3.1 resolution: "string-argv@npm:0.3.1" @@ -16668,6 +16696,15 @@ __metadata: languageName: node linkType: hard +"undici@npm:^5.21.2": + version: 5.21.2 + resolution: "undici@npm:5.21.2" + dependencies: + busboy: ^1.6.0 + checksum: baceaa9e610966631e86ad2869b657556dd465438eed55e8079cec2a306ecbeecfde2d6e37e43baf96a4c59588ebef50476131e96e018dcc0a7f5db7e6a06c85 + languageName: node + linkType: hard + "unfetch@npm:^4.2.0": version: 4.2.0 resolution: "unfetch@npm:4.2.0" diff --git a/apps/web/src/components/blocksuite/header/styles.ts b/apps/web/src/components/blocksuite/header/styles.ts index c9402c17e6..3d710176c0 100644 --- a/apps/web/src/components/blocksuite/header/styles.ts +++ b/apps/web/src/components/blocksuite/header/styles.ts @@ -31,7 +31,6 @@ export const StyledHeader = styled('div')<{ hasWarning: boolean }>( padding: '0 20px', ...displayFlex('space-between', 'center'), background: theme.colors.pageBackground, - transition: 'background-color 0.5s', zIndex: 99, position: 'relative', }; diff --git a/apps/web/src/shared/apis.ts b/apps/web/src/shared/apis.ts index 08b8947a6c..5ac83d7a56 100644 --- a/apps/web/src/shared/apis.ts +++ b/apps/web/src/shared/apis.ts @@ -12,8 +12,8 @@ import { jotaiStore } from '@affine/workspace/atom'; import { isValidIPAddress } from '../utils'; let prefixUrl = '/'; -if (typeof window === 'undefined') { - // SSR +if (typeof window === 'undefined' || environment.isDesktop) { + // SSR or Desktop const serverAPI = config.serverAPI; if (isValidIPAddress(serverAPI.split(':')[0])) { // This is for Server side rendering support @@ -21,6 +21,7 @@ if (typeof window === 'undefined') { } else { prefixUrl = serverAPI; } + prefixUrl = prefixUrl.endsWith('/') ? prefixUrl : prefixUrl + '/'; } else { const params = new URLSearchParams(window.location.search); params.get('prefixUrl') && (prefixUrl = params.get('prefixUrl') as string); diff --git a/packages/workspace/src/affine/login.ts b/packages/workspace/src/affine/login.ts index 445dde1db2..73114e976f 100644 --- a/packages/workspace/src/affine/login.ts +++ b/packages/workspace/src/affine/login.ts @@ -1,4 +1,5 @@ import { DebugLogger } from '@affine/debug'; +import { getEnvironment } from '@affine/env'; import { assertExists } from '@blocksuite/global/utils'; import { Slot } from '@blocksuite/store'; import { initializeApp } from 'firebase/app'; @@ -9,6 +10,7 @@ import { getAuth as getFirebaseAuth, GithubAuthProvider, GoogleAuthProvider, + signInWithCredential, signInWithPopup, } from 'firebase/auth'; import { decode } from 'js-base64'; @@ -64,6 +66,13 @@ export const setLoginStorage = (login: LoginResponse) => { ); }; +const signInWithElectron = async (firebaseAuth: FirebaseAuth) => { + const code = await window.apis?.googleSignIn(); + const credential = GoogleAuthProvider.credential(code); + const user = await signInWithCredential(firebaseAuth, credential); + return await user.user.getIdToken(); +}; + export const clearLoginStorage = () => { localStorage.removeItem(STORAGE_KEY); }; @@ -152,6 +161,7 @@ export function createAffineAuth(prefix = '/') { method: SignMethod ): Promise => { const auth = getAuth(); + const environment = getEnvironment(); if (!auth) { throw new Error('Failed to initialize firebase'); } @@ -167,9 +177,14 @@ export function createAffineAuth(prefix = '/') { throw new Error('Unsupported sign method'); } try { - const response = await signInWithPopup(auth, provider); - const idToken = await response.user.getIdToken(); - logger.debug(idToken); + let idToken: string | undefined; + if (environment.isDesktop) { + idToken = await signInWithElectron(auth); + } else { + const response = await signInWithPopup(auth, provider); + idToken = await response.user.getIdToken(); + } + logger.debug('idToken', idToken); return fetch(prefix + 'api/user/token', { method: 'POST', headers: { diff --git a/packages/workspace/src/type.ts b/packages/workspace/src/type.ts index d3645af239..1ca515c9a3 100644 --- a/packages/workspace/src/type.ts +++ b/packages/workspace/src/type.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// import type { Workspace as RemoteWorkspace } from '@affine/workspace/affine/api'; import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import type { FC, PropsWithChildren } from 'react';