fix: failed to load blobs in electron (#1927)

This commit is contained in:
Peng Xiao 2023-04-13 23:14:46 +08:00 committed by GitHub
parent 934e242116
commit 42756045bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 27 additions and 32 deletions

View File

@ -1,16 +1,10 @@
import type { RequestInit } from 'undici';
import { fetch, ProxyAgent } from 'undici';
const redirectUri = 'https://affine.pro/client/auth-callback'; 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&customParameters={"prompt":"select_account"}`; 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&customParameters={"prompt":"select_account"}`;
const tokenEndpoint = 'https://oauth2.googleapis.com/token'; const tokenEndpoint = 'https://oauth2.googleapis.com/token';
export const exchangeToken = async (code: string) => { export const getExchangeTokenParams = (code: string) => {
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;
const proxyAgent = httpProxy ? new ProxyAgent(httpProxy) : undefined;
const postData = { const postData = {
code, code,
client_id: process.env.AFFINE_GOOGLE_CLIENT_ID || '', client_id: process.env.AFFINE_GOOGLE_CLIENT_ID || '',
@ -18,15 +12,12 @@ export const exchangeToken = async (code: string) => {
redirect_uri: redirectUri, redirect_uri: redirectUri,
grant_type: 'authorization_code', grant_type: 'authorization_code',
}; };
const requestOptions: RequestInit = { const requestInit: RequestInit = {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
}, },
body: new URLSearchParams(postData).toString(), body: new URLSearchParams(postData).toString(),
dispatcher: proxyAgent,
}; };
return fetch(tokenEndpoint, requestOptions).then(response => { return { requestInit, url: tokenEndpoint };
return response.json();
});
}; };

View File

@ -7,7 +7,7 @@ import { BrowserWindow, ipcMain, nativeTheme } from 'electron';
import fs from 'fs-extra'; import fs from 'fs-extra';
import { parse } from 'url'; import { parse } from 'url';
import { exchangeToken, oauthEndpoint } from './google-auth'; import { getExchangeTokenParams, oauthEndpoint } from './google-auth';
const AFFINE_ROOT = path.join(os.homedir(), '.affine'); const AFFINE_ROOT = path.join(os.homedir(), '.affine');
@ -40,29 +40,24 @@ export const registerHandlers = () => {
logger.info('sidebar visibility change', visible); logger.info('sidebar visibility change', visible);
}); });
ipcMain.handle('ui:google-sign-in', async () => { ipcMain.handle('ui:get-google-oauth-code', async () => {
logger.info('starting google sign in ...'); logger.info('starting google sign in ...');
shell.openExternal(oauthEndpoint); shell.openExternal(oauthEndpoint);
return new Promise<string>((resolve, reject) => { return new Promise((resolve, reject) => {
const handleOpenUrl = async (_: any, url: string) => { const handleOpenUrl = async (_: any, url: string) => {
const mainWindow = BrowserWindow.getAllWindows().find( const mainWindow = BrowserWindow.getAllWindows().find(
w => !w.isDestroyed() w => !w.isDestroyed()
); );
const urlObj = parse(url.replace('??', '?'), true); const urlObj = parse(url.replace('??', '?'), true);
if (!mainWindow || !url.startsWith('affine://')) return; if (!mainWindow || !url.startsWith('affine://auth-callback')) return;
const code = urlObj.query['code'] as string; const code = urlObj.query['code'] as string;
if (!code) return; if (!code) return;
logger.info('google sign in code received from callback', code); logger.info('google sign in code received from callback', code);
const token = (await exchangeToken(code)) as {
id_token: string;
};
app.removeListener('open-url', handleOpenUrl); app.removeListener('open-url', handleOpenUrl);
resolve(token.id_token); resolve(getExchangeTokenParams(code));
logger.info('google sign in successful', token);
}; };
app.on('open-url', handleOpenUrl); app.on('open-url', handleOpenUrl);
@ -70,7 +65,7 @@ export const registerHandlers = () => {
setTimeout(() => { setTimeout(() => {
reject(new Error('Timed out')); reject(new Error('Timed out'));
app.removeListener('open-url', handleOpenUrl); app.removeListener('open-url', handleOpenUrl);
}, 60000); }, 30000);
}); });
}); });

View File

@ -36,6 +36,7 @@ export function registerProtocol() {
const realpath = toAbsolutePath(url); const realpath = toAbsolutePath(url);
// console.log('realpath', realpath, 'for', url); // console.log('realpath', realpath, 'for', url);
callback(realpath); callback(realpath);
return true;
}); });
protocol.registerFileProtocol('assets', (request, callback) => { protocol.registerFileProtocol('assets', (request, callback) => {
@ -43,6 +44,7 @@ export function registerProtocol() {
const realpath = toAbsolutePath(url); const realpath = toAbsolutePath(url);
// console.log('realpath', realpath, 'for', url); // console.log('realpath', realpath, 'for', url);
callback(realpath); callback(realpath);
return true;
}); });
} }
@ -60,7 +62,6 @@ export function registerProtocol() {
'DELETE', 'DELETE',
'OPTIONS', 'OPTIONS',
]; ];
// responseHeaders['Content-Security-Policy'] = ["default-src 'self'"];
} }
callback({ responseHeaders }); callback({ responseHeaders });

View File

@ -7,6 +7,6 @@ interface Window {
* *
* @see https://github.com/cawa-93/dts-for-context-bridge * @see https://github.com/cawa-93/dts-for-context-bridge
*/ */
readonly apis: { workspaceSync: (id: string) => Promise<any>; onThemeChange: (theme: string) => Promise<any>; onSidebarVisibilityChange: (visible: boolean) => Promise<any>; googleSignIn: () => Promise<string>; updateEnv: (env: string, value: string) => void; }; readonly apis: { workspaceSync: (id: string) => Promise<any>; onThemeChange: (theme: string) => Promise<any>; onSidebarVisibilityChange: (visible: boolean) => Promise<any>; getGoogleOauthCode: () => Promise<{ requestInit: RequestInit; url: string; }>; updateEnv: (env: string, value: string) => void; };
readonly appInfo: { electron: boolean; isMacOS: boolean; }; readonly appInfo: { electron: boolean; isMacOS: boolean; };
} }

View File

@ -31,9 +31,11 @@ contextBridge.exposeInMainWorld('apis', {
ipcRenderer.invoke('ui:sidebar-visibility-change', visible), ipcRenderer.invoke('ui:sidebar-visibility-change', visible),
/** /**
* Try sign in using Google and return a Google IDToken * Try sign in using Google and return a request object to exchange the code for a token
* Not exchange in Node side because it is easier to do it in the renderer with VPN
*/ */
googleSignIn: (): Promise<string> => ipcRenderer.invoke('ui:google-sign-in'), getGoogleOauthCode: (): Promise<{ requestInit: RequestInit; url: string }> =>
ipcRenderer.invoke('ui:get-google-oauth-code'),
/** /**
* Secret backdoor to update environment variables in main process * Secret backdoor to update environment variables in main process

View File

@ -52,7 +52,10 @@ const getPersistenceAllWorkspace = () => {
item.id, item.id,
(k: string) => (k: string) =>
// fixme: token could be expired // fixme: token could be expired
({ api: '/api/workspace', token: getLoginStorage()?.token }[k]) ({
api: prefixUrl + 'api/workspace',
token: getLoginStorage()?.token,
}[k])
); );
const affineWorkspace: AffineWorkspace = { const affineWorkspace: AffineWorkspace = {
...item, ...item,

View File

@ -67,10 +67,13 @@ export const setLoginStorage = (login: LoginResponse) => {
}; };
const signInWithElectron = async (firebaseAuth: FirebaseAuth) => { const signInWithElectron = async (firebaseAuth: FirebaseAuth) => {
const code = await window.apis?.googleSignIn(); if (window.apis) {
const credential = GoogleAuthProvider.credential(code); const { url, requestInit } = await window.apis.getGoogleOauthCode();
const user = await signInWithCredential(firebaseAuth, credential); const { id_token } = await fetch(url, requestInit).then(res => res.json());
return await user.user.getIdToken(); const credential = GoogleAuthProvider.credential(id_token);
const user = await signInWithCredential(firebaseAuth, credential);
return await user.user.getIdToken();
}
}; };
export const clearLoginStorage = () => { export const clearLoginStorage = () => {