mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
chore: move electron out of the renderer (#4406)
* fix lint * fix types * remove flush db.change event send * guard against tests running electron functions * copy preload to build output * fix webview context menu * fix context menu and plugin install * move installPlugin to main context
This commit is contained in:
parent
837342ddab
commit
3947bdc4aa
@ -247,12 +247,14 @@ export const database = {
|
||||
for (const fn of changeListeners) {
|
||||
await fn(changes);
|
||||
}
|
||||
|
||||
// Notify remote listeners
|
||||
const windows = electron.BrowserWindow.getAllWindows();
|
||||
const isMainContext = process.type === 'browser';
|
||||
if (isMainContext){
|
||||
const windows = electron.BrowserWindow.getAllWindows();
|
||||
|
||||
for (const window of windows) {
|
||||
window.webContents.send('db.changes', changes);
|
||||
for (const window of windows) {
|
||||
window.webContents.send('db.changes', changes);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -19,66 +19,18 @@ export function clickLink(href: string) {
|
||||
export const getPortableExecutableDir = () => process.env.PORTABLE_EXECUTABLE_DIR;
|
||||
|
||||
export function getDataDirectory() {
|
||||
const { app } = electron.remote || electron;
|
||||
const { app } = process.type === 'renderer' ? window : electron;
|
||||
return process.env.INSOMNIA_DATA_PATH || app.getPath('userData');
|
||||
}
|
||||
|
||||
export function getViewportSize(): string | null {
|
||||
const { BrowserWindow } = electron.remote || electron;
|
||||
const browserWindow = BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0];
|
||||
|
||||
if (browserWindow) {
|
||||
const { width, height } = browserWindow.getContentBounds();
|
||||
return `${width}x${height}`;
|
||||
} else {
|
||||
// No windows open
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function getScreenResolution() {
|
||||
const { screen } = electron.remote || electron;
|
||||
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
||||
return `${width}x${height}`;
|
||||
}
|
||||
|
||||
export function getUserLanguage() {
|
||||
const { app } = electron.remote || electron;
|
||||
return app.getLocale();
|
||||
}
|
||||
|
||||
export function getTempDir() {
|
||||
// NOTE: Using a fairly unique name here because "insomnia" is a common word
|
||||
const { app } = electron.remote || electron;
|
||||
const { app } = process.type === 'renderer' ? window : electron;
|
||||
const dir = join(app.getPath('temp'), `insomnia_${appConfig.version}`);
|
||||
mkdirp.sync(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
export function restartApp() {
|
||||
const { app } = electron.remote || electron;
|
||||
app.relaunch();
|
||||
app.exit();
|
||||
}
|
||||
|
||||
export const exitAppFailure = () => {
|
||||
const { app } = electron.remote || electron;
|
||||
app.exit(1);
|
||||
};
|
||||
|
||||
export const setMenuBarVisibility = (visible: boolean) => {
|
||||
const { BrowserWindow } = electron.remote || electron;
|
||||
BrowserWindow.getAllWindows()
|
||||
.forEach(window => {
|
||||
// the `setMenuBarVisibility` signature uses `visible` semantics
|
||||
window.setMenuBarVisibility(visible);
|
||||
|
||||
// the `setAutoHideMenu` signature uses `hide` semantics
|
||||
const hide = !visible;
|
||||
window.setAutoHideMenuBar(hide);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* There's no option that prevents Electron from fetching spellcheck dictionaries from Chromium's CDN and passing a non-resolving URL is the only known way to prevent it from fetching.
|
||||
* see: https://github.com/electron/electron/issues/22995
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { OpenDialogOptions, remote } from 'electron';
|
||||
import { OpenDialogOptions } from 'electron';
|
||||
import { unreachableCase } from 'ts-assert-unreachable';
|
||||
|
||||
interface Options {
|
||||
@ -49,7 +49,8 @@ export const selectFileOrFolder = async ({ itemTypes, extensions }: Options) =>
|
||||
}],
|
||||
};
|
||||
|
||||
const { canceled, filePaths } = await remote.dialog.showOpenDialog(options);
|
||||
const { canceled, filePaths } = await window.dialog.showOpenDialog(options);
|
||||
|
||||
const fileSelection: FileSelection = {
|
||||
filePath: filePaths[0],
|
||||
canceled,
|
||||
|
17
packages/insomnia-app/app/global.d.ts
vendored
17
packages/insomnia-app/app/global.d.ts
vendored
@ -22,6 +22,23 @@ declare namespace NodeJS {
|
||||
|
||||
interface Window {
|
||||
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__: Function;
|
||||
main: {
|
||||
restart: () => void;
|
||||
authorizeUserInWindow: (options: { url: string; urlSuccessRegex?: RegExp; urlFailureRegex?: RegExp; sessionId: string }) => Promise<string>;
|
||||
setMenuBarVisibility: (visible: boolean) => void;
|
||||
installPlugin: (url: string) => void;
|
||||
};
|
||||
dialog: {
|
||||
showOpenDialog: (options: Electron.OpenDialogOptions) => Promise<Electron.OpenDialogReturnValue>;
|
||||
showSaveDialog: (options: Electron.SaveDialogOptions) => Promise<Electron.SaveDialogReturnValue>;
|
||||
};
|
||||
app: {
|
||||
getPath: (name: string) => string;
|
||||
getAppPath: () => string;
|
||||
};
|
||||
shell: {
|
||||
showItemInFolder: (fullPath: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
// needed for @hot-loader/react-dom in order for TypeScript to build
|
||||
|
@ -7,7 +7,7 @@ import appConfig from '../config/config.json';
|
||||
import { SegmentEvent, trackSegmentEvent } from './common/analytics';
|
||||
import { changelogUrl, getAppVersion, isDevelopment, isMac } from './common/constants';
|
||||
import { database } from './common/database';
|
||||
import { disableSpellcheckerDownload, exitAppFailure } from './common/electron-helpers';
|
||||
import { disableSpellcheckerDownload } from './common/electron-helpers';
|
||||
import log, { initializeLogging } from './common/log';
|
||||
import { validateInsomniaConfig } from './common/validate-insomnia-config';
|
||||
import * as errorHandling from './main/error-handling';
|
||||
@ -17,6 +17,8 @@ import * as updates from './main/updates';
|
||||
import * as windowUtils from './main/window-utils';
|
||||
import * as models from './models/index';
|
||||
import type { Stats } from './models/stats';
|
||||
import { authorizeUserInWindow } from './network/o-auth-2/misc';
|
||||
import installPlugin from './plugins/install';
|
||||
import type { ToastNotification } from './ui/components/toast';
|
||||
|
||||
// Handle potential auto-update
|
||||
@ -41,7 +43,14 @@ if (!isDevelopment()) {
|
||||
// So if (window) checks don't throw
|
||||
global.window = global.window || undefined;
|
||||
|
||||
contextMenu();
|
||||
// setup right click menu
|
||||
app.on('web-contents-created', (_, contents) => {
|
||||
if (contents.getType() === 'webview') {
|
||||
contextMenu({ window: contents });
|
||||
} else {
|
||||
contextMenu();
|
||||
}
|
||||
});
|
||||
|
||||
// When the app is first launched
|
||||
app.on('ready', async () => {
|
||||
@ -50,7 +59,7 @@ app.on('ready', async () => {
|
||||
if (error) {
|
||||
electron.dialog.showErrorBox(error.title, error.message);
|
||||
console.log('[config] Insomnia config is invalid, preventing app initialization');
|
||||
exitAppFailure();
|
||||
app.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -203,6 +212,53 @@ async function _trackStats() {
|
||||
|
||||
trackSegmentEvent(SegmentEvent.appStarted, {}, { queueable: true });
|
||||
|
||||
ipcMain.handle('showOpenDialog', async (_, options: Electron.OpenDialogOptions) => {
|
||||
const { filePaths, canceled } = await electron.dialog.showOpenDialog(options);
|
||||
return { filePaths, canceled };
|
||||
});
|
||||
|
||||
ipcMain.handle('showSaveDialog', async (_, options: Electron.SaveDialogOptions) => {
|
||||
const { filePath, canceled } = await electron.dialog.showSaveDialog(options);
|
||||
return { filePath, canceled };
|
||||
});
|
||||
|
||||
ipcMain.handle('installPlugin', async (_, options) => {
|
||||
return installPlugin(options);
|
||||
});
|
||||
|
||||
ipcMain.on('showItemInFolder', (_, name) => {
|
||||
electron.shell.showItemInFolder(name);
|
||||
});
|
||||
|
||||
ipcMain.on('restart', () => {
|
||||
app.relaunch();
|
||||
app.exit();
|
||||
});
|
||||
|
||||
ipcMain.handle('setMenuBarVisibility', (_, visible) => {
|
||||
electron.BrowserWindow.getAllWindows()
|
||||
.forEach(window => {
|
||||
// the `setMenuBarVisibility` signature uses `visible` semantics
|
||||
window.setMenuBarVisibility(visible);
|
||||
// the `setAutoHideMenu` signature uses `hide` semantics
|
||||
const hide = !visible;
|
||||
window.setAutoHideMenuBar(hide);
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('getPath', (event, name) => {
|
||||
event.returnValue = electron.app.getPath(name);
|
||||
});
|
||||
|
||||
ipcMain.on('getAppPath', event => {
|
||||
event.returnValue = electron.app.getAppPath();
|
||||
});
|
||||
|
||||
ipcMain.handle('authorizeUserInWindow', (_, options) => {
|
||||
const { url, urlSuccessRegex, urlFailureRegex, sessionId } = options;
|
||||
return authorizeUserInWindow({ url, urlSuccessRegex, urlFailureRegex, sessionId });
|
||||
});
|
||||
|
||||
ipcMain.once('window-ready', () => {
|
||||
const { currentVersion, launches, lastVersion } = stats;
|
||||
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
MNEMONIC_SYM,
|
||||
} from '../common/constants';
|
||||
import { docsBase } from '../common/documentation';
|
||||
import { clickLink, getDataDirectory, restartApp } from '../common/electron-helpers';
|
||||
import { clickLink, getDataDirectory } from '../common/electron-helpers';
|
||||
import * as log from '../common/log';
|
||||
import LocalStorage from './local-storage';
|
||||
|
||||
@ -85,10 +85,10 @@ export function createWindow() {
|
||||
acceptFirstMouse: true,
|
||||
icon: path.resolve(__dirname, appLogo),
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
zoomFactor: zoomFactor,
|
||||
nodeIntegration: true,
|
||||
webviewTag: true,
|
||||
enableRemoteModule: true,
|
||||
// TODO: enable context isolation
|
||||
contextIsolation: false,
|
||||
disableBlinkFeatures: 'Auxclick',
|
||||
@ -482,7 +482,7 @@ export function createWindow() {
|
||||
},
|
||||
{
|
||||
label: `R${MNEMONIC_SYM}estart`,
|
||||
click: restartApp,
|
||||
click: window?.main.restart,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -1,9 +1,10 @@
|
||||
import electron from 'electron';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { globalBeforeEach } from '../../__jest__/before-each';
|
||||
import { buildMultipart, DEFAULT_BOUNDARY } from '../multipart';
|
||||
|
||||
window.app = electron.app;
|
||||
describe('buildMultipart()', () => {
|
||||
beforeEach(globalBeforeEach);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { CurlHttpVersion } from '@getinsomnia/node-libcurl';
|
||||
import electron from 'electron';
|
||||
import fs from 'fs';
|
||||
import { HttpVersions } from 'insomnia-common';
|
||||
import { join as pathJoin, resolve as pathResolve } from 'path';
|
||||
@ -18,6 +19,7 @@ import { getRenderedRequestAndContext } from '../../common/render';
|
||||
import * as models from '../../models';
|
||||
import { DEFAULT_BOUNDARY } from '../multipart';
|
||||
import * as networkUtils from '../network';
|
||||
window.app = electron.app;
|
||||
|
||||
const getRenderedRequest = async (args: Parameters<typeof getRenderedRequestAndContext>[0]) => (await getRenderedRequestAndContext(args)).request;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as electron from 'electron';
|
||||
import fs from 'fs';
|
||||
import mimes from 'mime-types';
|
||||
import path from 'path';
|
||||
@ -15,7 +14,7 @@ interface Multipart {
|
||||
|
||||
export async function buildMultipart(params: RequestBodyParameter[]) {
|
||||
return new Promise<Multipart>(async (resolve, reject) => {
|
||||
const filePath = path.join(electron.remote.app.getPath('temp'), Math.random() + '.body');
|
||||
const filePath = path.join(window.app.getPath('temp'), Math.random() + '.body');
|
||||
const writeStream = fs.createWriteStream(filePath);
|
||||
const lineBreak = '\r\n';
|
||||
let totalSize = 0;
|
||||
|
@ -5,7 +5,6 @@ import { globalBeforeEach } from '../../../__jest__/before-each';
|
||||
import { getTempDir } from '../../../common/electron-helpers';
|
||||
import * as network from '../../network';
|
||||
import getToken from '../grant-authorization-code';
|
||||
import { createBWRedirectMock } from './helpers';
|
||||
|
||||
// Mock some test things
|
||||
const AUTHORIZE_URL = 'https://foo.com/authorizeAuthCode';
|
||||
@ -22,7 +21,7 @@ describe('authorization_code', () => {
|
||||
beforeEach(globalBeforeEach);
|
||||
|
||||
it('gets token with JSON and basic auth', async () => {
|
||||
createBWRedirectMock({ redirectTo: `${REDIRECT_URI}?code=code_123&state=${STATE}` });
|
||||
window.main = { authorizeUserInWindow: () => Promise.resolve(`${REDIRECT_URI}?code=code_123&state=${STATE}`) };
|
||||
const bodyPath = path.join(getTempDir(), 'foo.response');
|
||||
fs.writeFileSync(
|
||||
bodyPath,
|
||||
@ -130,7 +129,7 @@ describe('authorization_code', () => {
|
||||
});
|
||||
|
||||
it('gets token with urlencoded and body auth', async () => {
|
||||
createBWRedirectMock({ redirectTo: `${REDIRECT_URI}?code=code_123&state=${STATE}` });
|
||||
window.main = { authorizeUserInWindow: () => Promise.resolve(`${REDIRECT_URI}?code=code_123&state=${STATE}`) };
|
||||
const bodyPath = path.join(getTempDir(), 'foo.response');
|
||||
fs.writeFileSync(
|
||||
bodyPath,
|
||||
@ -242,7 +241,7 @@ describe('authorization_code', () => {
|
||||
});
|
||||
|
||||
it('uses PKCE', async () => {
|
||||
createBWRedirectMock({ redirectTo: `${REDIRECT_URI}?code=code_123&state=${STATE}` });
|
||||
window.main = { authorizeUserInWindow: () => Promise.resolve(`${REDIRECT_URI}?code=code_123&state=${STATE}`) };
|
||||
const bodyPath = path.join(getTempDir(), 'foo.response');
|
||||
fs.writeFileSync(
|
||||
bodyPath,
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { globalBeforeEach } from '../../../__jest__/before-each';
|
||||
import getToken from '../grant-implicit';
|
||||
import { createBWRedirectMock } from './helpers';
|
||||
// Mock some test things
|
||||
const AUTHORIZE_URL = 'https://foo.com/authorizeAuthCode';
|
||||
const CLIENT_ID = 'client_123';
|
||||
@ -13,7 +12,8 @@ describe('implicit', () => {
|
||||
beforeEach(globalBeforeEach);
|
||||
|
||||
it('works in default case', async () => {
|
||||
createBWRedirectMock({ redirectTo: `${REDIRECT_URI}#access_token=token_123&state=${STATE}&foo=bar` });
|
||||
window.main = { authorizeUserInWindow: () => Promise.resolve(`${REDIRECT_URI}#access_token=token_123&state=${STATE}&foo=bar`) };
|
||||
|
||||
const result = await getToken(AUTHORIZE_URL, CLIENT_ID, REDIRECT_URI, SCOPE, STATE, AUDIENCE);
|
||||
expect(result).toEqual({
|
||||
access_token: 'token_123',
|
||||
|
@ -10,7 +10,7 @@ export function createBWRedirectMock({
|
||||
redirectTo,
|
||||
setCertificateVerifyProc = () => {},
|
||||
}: Options) {
|
||||
electron.remote.BrowserWindow = jest.fn(function() {
|
||||
electron.BrowserWindow = jest.fn(function() {
|
||||
this._emitter = new EventEmitter();
|
||||
|
||||
this.loadURL = () => this.webContents.emit('did-navigate');
|
||||
|
@ -64,6 +64,8 @@ describe('authorizeUserInWindow()', () => {
|
||||
|
||||
const getCertificateVerifyCallbackMock = () => {
|
||||
const mockCallback = mocked<(verificationResult: number) => void>(jest.fn());
|
||||
window.main = { authorizeUserInWindow: () => Promise.resolve(MOCK_AUTHORIZATION_URL) };
|
||||
|
||||
createBWRedirectMock({
|
||||
redirectTo: MOCK_AUTHORIZATION_URL,
|
||||
setCertificateVerifyProc: proc => {
|
||||
@ -86,7 +88,7 @@ describe('authorizeUserInWindow()', () => {
|
||||
|
||||
// Act
|
||||
// We don't really care about the result here, since we're only testing an event handler.
|
||||
await authorizeUserInWindow(MOCK_AUTHORIZATION_URL, /.*/);
|
||||
await authorizeUserInWindow({ url: MOCK_AUTHORIZATION_URL, urlSuccessRegex: /.*/ });
|
||||
|
||||
// Assert
|
||||
expect(mockCallback).toHaveBeenCalledWith(ChromiumVerificationResult.USE_CHROMIUM_RESULT);
|
||||
@ -103,7 +105,7 @@ describe('authorizeUserInWindow()', () => {
|
||||
|
||||
// Act
|
||||
// We don't really care about the result here, since we're only testing an event handler.
|
||||
await authorizeUserInWindow(MOCK_AUTHORIZATION_URL, /.*/);
|
||||
await authorizeUserInWindow({ url: MOCK_AUTHORIZATION_URL, urlSuccessRegex: /.*/ });
|
||||
|
||||
// Assert
|
||||
expect(mockCallback).toHaveBeenCalledWith(ChromiumVerificationResult.BLIND_TRUST);
|
||||
|
@ -7,8 +7,7 @@ import * as models from '../../models/index';
|
||||
import { getBasicAuthHeader } from '../basic-auth/get-header';
|
||||
import { sendWithSettings } from '../network';
|
||||
import * as c from './constants';
|
||||
import { authorizeUserInWindow, responseToObject } from './misc';
|
||||
|
||||
import { getOAuthSession, responseToObject } from './misc';
|
||||
export default async function(
|
||||
requestId: string,
|
||||
authorizeUrl: string,
|
||||
@ -147,9 +146,11 @@ async function _authorize(
|
||||
// Add query params to URL
|
||||
const qs = buildQueryStringFromParams(params);
|
||||
const finalUrl = joinUrlAndQueryString(url, qs);
|
||||
const successRegex = new RegExp(`${escapeRegex(redirectUri)}.*(code=)`, 'i');
|
||||
const failureRegex = new RegExp(`${escapeRegex(redirectUri)}.*(error=)`, 'i');
|
||||
const redirectedTo = await authorizeUserInWindow(finalUrl, successRegex, failureRegex);
|
||||
const urlSuccessRegex = new RegExp(`${escapeRegex(redirectUri)}.*(code=)`, 'i');
|
||||
const urlFailureRegex = new RegExp(`${escapeRegex(redirectUri)}.*(error=)`, 'i');
|
||||
const sessionId = getOAuthSession();
|
||||
|
||||
const redirectedTo = await window.main.authorizeUserInWindow({ url: finalUrl, urlSuccessRegex, urlFailureRegex, sessionId });
|
||||
console.log('[oauth2] Detected redirect ' + redirectedTo);
|
||||
const { query } = urlParse(redirectedTo);
|
||||
return responseToObject(query, [
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { buildQueryStringFromParams, joinUrlAndQueryString } from 'insomnia-url';
|
||||
|
||||
import * as c from './constants';
|
||||
import { authorizeUserInWindow, responseToObject } from './misc';
|
||||
import { getOAuthSession, responseToObject } from './misc';
|
||||
|
||||
export default async function(
|
||||
_requestId: string,
|
||||
@ -60,11 +60,11 @@ export default async function(
|
||||
// Add query params to URL
|
||||
const qs = buildQueryStringFromParams(params);
|
||||
const finalUrl = joinUrlAndQueryString(authorizationUrl, qs);
|
||||
const redirectedTo = await authorizeUserInWindow(
|
||||
finalUrl,
|
||||
/(access_token=|id_token=)/,
|
||||
/(error=)/,
|
||||
);
|
||||
const urlSuccessRegex = /(access_token=|id_token=)/;
|
||||
const urlFailureRegex = /(error=)/;
|
||||
const sessionId = getOAuthSession();
|
||||
const redirectedTo = await window.main.authorizeUserInWindow({ url: finalUrl, urlSuccessRegex, urlFailureRegex, sessionId });
|
||||
|
||||
const fragment = redirectedTo.split('#')[1];
|
||||
|
||||
if (fragment) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import electron from 'electron';
|
||||
import { BrowserWindow } from 'electron';
|
||||
import querystring from 'querystring';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
@ -9,20 +9,17 @@ export enum ChromiumVerificationResult {
|
||||
USE_CHROMIUM_RESULT = -3
|
||||
}
|
||||
|
||||
const LOCALSTORAGE_KEY_SESSION_ID = 'insomnia::current-oauth-session-id';
|
||||
let authWindowSessionId;
|
||||
|
||||
if (window.localStorage.getItem(LOCALSTORAGE_KEY_SESSION_ID)) {
|
||||
authWindowSessionId = window.localStorage.getItem(LOCALSTORAGE_KEY_SESSION_ID);
|
||||
} else {
|
||||
initNewOAuthSession();
|
||||
export const LOCALSTORAGE_KEY_SESSION_ID = 'insomnia::current-oauth-session-id';
|
||||
export function getOAuthSession(): string {
|
||||
const token = window.localStorage.getItem(LOCALSTORAGE_KEY_SESSION_ID);
|
||||
return token || initNewOAuthSession();
|
||||
}
|
||||
|
||||
export function initNewOAuthSession() {
|
||||
// the value of this variable needs to start with 'persist:'
|
||||
// otherwise sessions won't be persisted over application-restarts
|
||||
authWindowSessionId = `persist:oauth2_${uuid.v4()}`;
|
||||
const authWindowSessionId = `persist:oauth2_${uuid.v4()}`;
|
||||
window.localStorage.setItem(LOCALSTORAGE_KEY_SESSION_ID, authWindowSessionId);
|
||||
return authWindowSessionId;
|
||||
}
|
||||
|
||||
export function responseToObject(body, keys, defaults = {}) {
|
||||
@ -60,11 +57,12 @@ export function responseToObject(body, keys, defaults = {}) {
|
||||
return results;
|
||||
}
|
||||
|
||||
export function authorizeUserInWindow(
|
||||
export function authorizeUserInWindow({
|
||||
url,
|
||||
urlSuccessRegex = /(code=).*/,
|
||||
urlFailureRegex = /(error=).*/,
|
||||
) {
|
||||
sessionId,
|
||||
}) {
|
||||
return new Promise<string>(async (resolve, reject) => {
|
||||
let finalUrl: string | null = null;
|
||||
|
||||
@ -74,10 +72,10 @@ export function authorizeUserInWindow(
|
||||
} = await models.settings.getOrCreate();
|
||||
|
||||
// Create a child window
|
||||
const child = new electron.remote.BrowserWindow({
|
||||
const child = new BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
partition: authWindowSessionId,
|
||||
partition: sessionId,
|
||||
},
|
||||
show: false,
|
||||
});
|
||||
|
@ -146,7 +146,7 @@ export function init(renderPurpose: RenderPurpose = RENDER_PURPOSE_GENERAL): {
|
||||
getPath(name: string) {
|
||||
switch (name.toLowerCase()) {
|
||||
case 'desktop':
|
||||
return electron.remote.app.getPath('desktop');
|
||||
return window.app.getPath('desktop');
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown path name ${name}`);
|
||||
@ -172,7 +172,7 @@ export function init(renderPurpose: RenderPurpose = RENDER_PURPOSE_GENERAL): {
|
||||
buttonLabel: 'Save',
|
||||
defaultPath: options.defaultPath,
|
||||
};
|
||||
const { filePath } = await electron.remote.dialog.showSaveDialog(
|
||||
const { filePath } = await window.dialog.showSaveDialog(
|
||||
saveOptions
|
||||
);
|
||||
return filePath || null;
|
||||
|
@ -56,10 +56,11 @@ export default async function(lookupName: string) {
|
||||
mkdirp.sync(pluginDir);
|
||||
|
||||
// Download the module
|
||||
const request = electron.remote.net.request(info.dist.tarball);
|
||||
const request = electron.net.request(info.dist.tarball);
|
||||
request.on('error', err => {
|
||||
reject(new Error(`Failed to make plugin request ${info?.dist.tarball}: ${err.message}`));
|
||||
});
|
||||
|
||||
const { tmpDir } = await _installPluginToTmpDir(lookupName);
|
||||
console.log(`[plugins] Moving plugin from ${tmpDir} to ${pluginDir}`);
|
||||
|
||||
@ -247,7 +248,7 @@ export function isDeprecatedDependencies(str: string) {
|
||||
}
|
||||
|
||||
function _getYarnPath() {
|
||||
const { app } = electron.remote || electron;
|
||||
const { app } = process.type === 'renderer' ? window : electron;
|
||||
|
||||
// TODO: This is brittle. Make finding this more robust.
|
||||
if (isDevelopment()) {
|
||||
|
32
packages/insomnia-app/app/preload.js
Normal file
32
packages/insomnia-app/app/preload.js
Normal file
@ -0,0 +1,32 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
const main = {
|
||||
restart: () => ipcRenderer.send('restart'),
|
||||
authorizeUserInWindow: options => ipcRenderer.invoke('authorizeUserInWindow', options),
|
||||
setMenuBarVisibility: options => ipcRenderer.send('setMenuBarVisibility', options),
|
||||
installPlugin: options => ipcRenderer.invoke('installPlugin', options),
|
||||
};
|
||||
const dialog = {
|
||||
showOpenDialog: options => ipcRenderer.invoke('showOpenDialog', options),
|
||||
showSaveDialog: options => ipcRenderer.invoke('showSaveDialog', options),
|
||||
};
|
||||
const app = {
|
||||
getPath: options => ipcRenderer.sendSync('getPath', options),
|
||||
getAppPath: options => ipcRenderer.sendSync('getAppPath', options),
|
||||
};
|
||||
const shell = {
|
||||
showItemInFolder: options => ipcRenderer.send('showItemInFolder', options),
|
||||
};
|
||||
|
||||
// if (process.contextIsolated) { TODO: use if rather than try after upgrading to electron 13
|
||||
try {
|
||||
contextBridge.exposeInMainWorld('main', main);
|
||||
contextBridge.exposeInMainWorld('dialog', dialog);
|
||||
contextBridge.exposeInMainWorld('app', app);
|
||||
contextBridge.exposeInMainWorld('shell', shell);
|
||||
} catch {
|
||||
window.main = main;
|
||||
window.dialog = dialog;
|
||||
window.app = app;
|
||||
window.shell = shell;
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import electron from 'electron';
|
||||
import fs from 'fs';
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
@ -26,7 +25,7 @@ export class FileEditor extends PureComponent<Props> {
|
||||
render() {
|
||||
const { path } = this.props;
|
||||
// Replace home path with ~/ to make the path shorter
|
||||
const homeDirectory = electron.remote.app.getPath('home');
|
||||
const homeDirectory = window.app.getPath('home');
|
||||
const pathDescription = path.replace(homeDirectory, '~');
|
||||
let sizeDescription = '';
|
||||
|
||||
|
@ -3,7 +3,7 @@ import classnames from 'classnames';
|
||||
import { EditorFromTextArea, LintOptions, ShowHintOptions, TextMarker } from 'codemirror';
|
||||
import { GraphQLInfoOptions } from 'codemirror-graphql/info';
|
||||
import { ModifiedGraphQLJumpOptions } from 'codemirror-graphql/jump';
|
||||
import electron, { OpenDialogOptions } from 'electron';
|
||||
import { OpenDialogOptions } from 'electron';
|
||||
import { readFileSync } from 'fs';
|
||||
import { DefinitionNode, DocumentNode, GraphQLNonNull, GraphQLSchema, NonNullTypeNode, OperationDefinitionNode } from 'graphql';
|
||||
import { parse, typeFromAST } from 'graphql';
|
||||
@ -362,7 +362,7 @@ export class GraphQLEditor extends PureComponent<Props, State> {
|
||||
],
|
||||
};
|
||||
|
||||
const { canceled, filePaths } = await electron.remote.dialog.showOpenDialog(options);
|
||||
const { canceled, filePaths } = await window.dialog.showOpenDialog(options);
|
||||
|
||||
if (canceled) {
|
||||
return;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import classnames from 'classnames';
|
||||
import { clipboard, remote } from 'electron';
|
||||
import { clipboard } from 'electron';
|
||||
import fs from 'fs';
|
||||
import { HotKeyRegistry } from 'insomnia-common';
|
||||
import { json as jsonPrettify } from 'insomnia-prettify';
|
||||
@ -85,7 +85,7 @@ export class ResponsePane extends PureComponent<Props> {
|
||||
|
||||
const { contentType } = response;
|
||||
const extension = mime.extension(contentType) || 'unknown';
|
||||
const { canceled, filePath: outputPath } = await remote.dialog.showSaveDialog({
|
||||
const { canceled, filePath: outputPath } = await window.dialog.showSaveDialog({
|
||||
title: 'Save Response Body',
|
||||
buttonLabel: 'Save',
|
||||
defaultPath: `${request.name.replace(/ +/g, '_')}-${Date.now()}.${extension}`,
|
||||
@ -140,7 +140,7 @@ export class ResponsePane extends PureComponent<Props> {
|
||||
.map(v => v.value)
|
||||
.join('');
|
||||
|
||||
const { canceled, filePath } = await remote.dialog.showSaveDialog({
|
||||
const { canceled, filePath } = await window.dialog.showSaveDialog({
|
||||
title: 'Save Full Response',
|
||||
buttonLabel: 'Save',
|
||||
defaultPath: `${request.name.replace(/ +/g, '_')}-${Date.now()}.txt`,
|
||||
@ -192,7 +192,7 @@ export class ResponsePane extends PureComponent<Props> {
|
||||
const data = await exportHarCurrentRequest(request, response);
|
||||
const har = JSON.stringify(data, null, '\t');
|
||||
|
||||
const { filePath } = await remote.dialog.showSaveDialog({
|
||||
const { filePath } = await window.dialog.showSaveDialog({
|
||||
title: 'Export As HAR',
|
||||
buttonLabel: 'Save',
|
||||
defaultPath: `${request.name.replace(/ +/g, '_')}-${Date.now()}.har`,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import { OpenDialogOptions, remote } from 'electron';
|
||||
import { OpenDialogOptions } from 'electron';
|
||||
import { HotKeyRegistry } from 'insomnia-common';
|
||||
import React, { PureComponent, ReactNode } from 'react';
|
||||
|
||||
@ -123,7 +123,7 @@ export class RequestUrlBar extends PureComponent<Props, State> {
|
||||
buttonLabel: 'Select',
|
||||
properties: ['openDirectory'],
|
||||
};
|
||||
const { canceled, filePaths } = await remote.dialog.showOpenDialog(options);
|
||||
const { canceled, filePaths } = await window.dialog.showOpenDialog(options);
|
||||
|
||||
if (canceled) {
|
||||
return;
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import * as electron from 'electron';
|
||||
import { PluginConfig } from 'insomnia-common';
|
||||
import { Button, ToggleSwitch } from 'insomnia-components';
|
||||
import * as path from 'path';
|
||||
@ -18,7 +17,6 @@ import type { Settings } from '../../../models/settings';
|
||||
import { createPlugin } from '../../../plugins/create';
|
||||
import type { Plugin } from '../../../plugins/index';
|
||||
import { getPlugins } from '../../../plugins/index';
|
||||
import installPlugin from '../../../plugins/install';
|
||||
import { reload } from '../../../templating/index';
|
||||
import { CopyButton } from '../base/copy-button';
|
||||
import { Link } from '../base/link';
|
||||
@ -76,7 +74,7 @@ export class Plugins extends PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
try {
|
||||
await installPlugin(this.state.npmPluginValue.trim());
|
||||
await window.main.installPlugin(this.state.npmPluginValue.trim());
|
||||
await this._handleRefreshPlugins();
|
||||
newState.npmPluginValue = ''; // Clear input if successful install
|
||||
} catch (err) {
|
||||
@ -88,7 +86,7 @@ export class Plugins extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
static _handleOpenDirectory(directory: string) {
|
||||
electron.remote.shell.showItemInFolder(directory);
|
||||
window.shell.showItemInFolder(directory);
|
||||
}
|
||||
|
||||
async _handleRefreshPlugins() {
|
||||
@ -116,7 +114,7 @@ export class Plugins extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
static _handleClickShowPluginsFolder() {
|
||||
electron.remote.shell.showItemInFolder(PLUGIN_PATH);
|
||||
window.shell.showItemInFolder(PLUGIN_PATH);
|
||||
}
|
||||
|
||||
_handleCreatePlugin() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import electron, { SaveDialogOptions } from 'electron';
|
||||
import { SaveDialogOptions } from 'electron';
|
||||
import fs from 'fs';
|
||||
import mimes from 'mime-types';
|
||||
import moment from 'moment';
|
||||
@ -132,7 +132,7 @@ export class ResponseMultipartViewer extends PureComponent<Props, State> {
|
||||
const contentType = getContentTypeFromHeaders(part.headers, 'text/plain');
|
||||
const extension = mimes.extension(contentType) || '.txt';
|
||||
const lastDir = window.localStorage.getItem('insomnia.lastExportPath');
|
||||
const dir = lastDir || electron.remote.app.getPath('desktop');
|
||||
const dir = lastDir || window.app.getPath('desktop');
|
||||
const date = moment().format('YYYY-MM-DD');
|
||||
const filename = part.filename || `${part.name}_${date}`;
|
||||
const options: SaveDialogOptions = {
|
||||
@ -146,7 +146,7 @@ export class ResponseMultipartViewer extends PureComponent<Props, State> {
|
||||
},
|
||||
],
|
||||
};
|
||||
const { canceled, filePath } = await electron.remote.dialog.showSaveDialog(options);
|
||||
const { canceled, filePath } = await window.dialog.showSaveDialog(options);
|
||||
|
||||
if (canceled) {
|
||||
return;
|
||||
|
@ -1,6 +1,4 @@
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import contextMenu from 'electron-context-menu';
|
||||
import { EventEmitter } from 'events';
|
||||
import React, { createRef, PureComponent } from 'react';
|
||||
|
||||
import { AUTOBIND_CFG } from '../../../common/constants';
|
||||
@ -37,11 +35,6 @@ export class ResponseWebView extends PureComponent<Props> {
|
||||
|
||||
this.webview.current.removeEventListener('dom-ready', this._handleDOMReady);
|
||||
|
||||
contextMenu({
|
||||
// @ts-expect-error -- TSCONVERSION type mismatch
|
||||
window: this.webview.current,
|
||||
});
|
||||
|
||||
this._setBody();
|
||||
}
|
||||
|
||||
@ -59,17 +52,11 @@ export class ResponseWebView extends PureComponent<Props> {
|
||||
//
|
||||
// https://github.com/electron/electron/issues/20700
|
||||
//
|
||||
// webview.loadURL(`data:${contentType},${encodeURIComponent(body)}`, {
|
||||
// this.webview.current.loadURL(`data:${contentType},${encodeURIComponent(body)}`, {
|
||||
// baseURLForDataURL: url,
|
||||
// });
|
||||
// @ts-expect-error -- TSCONVERSION type mismatch
|
||||
this.webview.current.loadURL(`data:${contentType},${encodeURIComponent(bodyWithBase)}`);
|
||||
|
||||
// This is kind of hacky but electron-context-menu fails to save images if this isn't here.
|
||||
// @ts-expect-error -- TSCONVERSION type mismatch
|
||||
this.webview.current.webContents = this.webview.current;
|
||||
// @ts-expect-error -- TSCONVERSION type mismatch
|
||||
this.webview.current.webContents.session = new EventEmitter();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import { clipboard, ipcRenderer, remote, SaveDialogOptions } from 'electron';
|
||||
import { clipboard, ipcRenderer, SaveDialogOptions } from 'electron';
|
||||
import fs from 'fs';
|
||||
import HTTPSnippet from 'httpsnippet';
|
||||
import * as mime from 'mime-types';
|
||||
@ -681,7 +681,7 @@ class App extends PureComponent<AppProps, State> {
|
||||
options.defaultPath = defaultPath;
|
||||
}
|
||||
|
||||
const { filePath } = await remote.dialog.showSaveDialog(options);
|
||||
const { filePath } = await window.dialog.showSaveDialog(options);
|
||||
// @ts-expect-error -- TSCONVERSION don't set item if filePath is undefined
|
||||
window.localStorage.setItem('insomnia.sendAndDownloadLocation', filePath);
|
||||
return filePath || null;
|
||||
|
@ -2,7 +2,6 @@ import { useEffect, useLayoutEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { usePrevious } from 'react-use';
|
||||
|
||||
import { restartApp, setMenuBarVisibility } from '../../common/electron-helpers';
|
||||
import { Settings } from '../../models/settings';
|
||||
import { selectSettings } from '../redux/selectors';
|
||||
|
||||
@ -22,7 +21,7 @@ const useRestartSetting = (setting: keyof Settings) => {
|
||||
return;
|
||||
}
|
||||
|
||||
restartApp();
|
||||
window.main.restart();
|
||||
}, [nextValue, previousValue]);
|
||||
};
|
||||
|
||||
@ -49,7 +48,7 @@ export const useSettingsSideEffects = () => {
|
||||
}, [settings.fontSize]);
|
||||
|
||||
useEffect(() => {
|
||||
setMenuBarVisibility(!settings.autoHideMenuBar);
|
||||
window.main.setMenuBarVisibility(!settings.autoHideMenuBar);
|
||||
}, [settings.autoHideMenuBar]);
|
||||
|
||||
useRestartSetting('nunjucksPowerUserMode');
|
||||
|
@ -1,4 +1,3 @@
|
||||
import electron from 'electron';
|
||||
import fs, { NoParamCallback } from 'fs';
|
||||
import moment from 'moment';
|
||||
import path from 'path';
|
||||
@ -30,7 +29,6 @@ import { Request } from '../../../models/request';
|
||||
import { isWorkspace } from '../../../models/workspace';
|
||||
import { reloadPlugins } from '../../../plugins';
|
||||
import { createPlugin } from '../../../plugins/create';
|
||||
import install from '../../../plugins/install';
|
||||
import { setTheme } from '../../../plugins/misc';
|
||||
import { AskModal } from '../../../ui/components/modals/ask-modal';
|
||||
import { AlertModal } from '../../components/modals/alert-modal';
|
||||
@ -240,7 +238,7 @@ export const newCommand = (command: string, args: any) => async (dispatch: Dispa
|
||||
}
|
||||
|
||||
try {
|
||||
await install(args.name);
|
||||
await window.main.installPlugin(args.name);
|
||||
showModal(SettingsModal, TAB_INDEX_PLUGINS);
|
||||
} catch (err) {
|
||||
showError({
|
||||
@ -416,13 +414,13 @@ const showSaveExportedFileDialog = async ({
|
||||
const date = moment().format('YYYY-MM-DD');
|
||||
const name = exportedFileNamePrefix.replace(/ /g, '-');
|
||||
const lastDir = window.localStorage.getItem('insomnia.lastExportPath');
|
||||
const dir = lastDir || electron.remote.app.getPath('desktop');
|
||||
const dir = lastDir || window.app.getPath('desktop');
|
||||
const options = {
|
||||
title: 'Export Insomnia Data',
|
||||
buttonLabel: 'Export',
|
||||
defaultPath: `${path.join(dir, `${name}_${date}`)}.${selectedFormat}`,
|
||||
};
|
||||
const { filePath } = await electron.remote.dialog.showSaveDialog(options);
|
||||
const { filePath } = await window.dialog.showSaveDialog(options);
|
||||
return filePath || null;
|
||||
};
|
||||
|
||||
|
@ -89,7 +89,7 @@ export const importFile = (
|
||||
},
|
||||
],
|
||||
};
|
||||
const { canceled, filePaths } = await electron.remote.dialog.showOpenDialog(openDialogOptions);
|
||||
const { canceled, filePaths } = await window.dialog.showOpenDialog(openDialogOptions);
|
||||
|
||||
if (canceled) {
|
||||
// It was cancelled, so let's bail out
|
||||
|
16
packages/insomnia-app/package-lock.json
generated
16
packages/insomnia-app/package-lock.json
generated
@ -11244,13 +11244,13 @@
|
||||
}
|
||||
},
|
||||
"electron-context-menu": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-2.5.2.tgz",
|
||||
"integrity": "sha512-1cEQR6fA9ktFsRBc+eXPwvrOgAPytUD7rUV4iBAA5zTrLAPKokJ23xeMjcK2fjrDPrlFRBxcLz0KP+GUhMrSCQ==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-3.1.1.tgz",
|
||||
"integrity": "sha512-LJhwaKf6XHwk2LQ5SdwoGNODoA8lRwks9bbEeAqqMf4e3hsrT7pZtX6MaHKYNFZKxF14JjI/VR+VRjGvxmaQoA==",
|
||||
"requires": {
|
||||
"cli-truncate": "^2.1.0",
|
||||
"electron-dl": "^3.1.0",
|
||||
"electron-is-dev": "^1.2.0"
|
||||
"electron-dl": "^3.2.1",
|
||||
"electron-is-dev": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"electron-devtools-installer": {
|
||||
@ -11293,9 +11293,9 @@
|
||||
}
|
||||
},
|
||||
"electron-is-dev": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz",
|
||||
"integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw=="
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-2.0.0.tgz",
|
||||
"integrity": "sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA=="
|
||||
},
|
||||
"electron-log": {
|
||||
"version": "4.4.3",
|
||||
|
@ -91,7 +91,7 @@
|
||||
"codemirror": "^5.62.3",
|
||||
"codemirror-graphql": "^1.0.2",
|
||||
"color": "^3.1.2",
|
||||
"electron-context-menu": "^2.5.2",
|
||||
"electron-context-menu": "^3.1.1",
|
||||
"electron-log": "^4.4.3",
|
||||
"electron-updater": "^4.6.1",
|
||||
"framer-motion": "^1.11.1",
|
||||
|
@ -30,7 +30,7 @@ if (process.env.NODE_ENV === 'development') {
|
||||
plugins = productionConfig.plugins;
|
||||
}
|
||||
|
||||
const configuration: Configuration = {
|
||||
const configuration: Configuration[] = [{
|
||||
...productionConfig,
|
||||
devtool,
|
||||
entry: ['./main.development.ts'],
|
||||
@ -40,6 +40,14 @@ const configuration: Configuration = {
|
||||
},
|
||||
target: 'electron-main',
|
||||
plugins,
|
||||
};
|
||||
},
|
||||
{
|
||||
entry: './app/preload.js',
|
||||
target: 'electron-preload',
|
||||
output: {
|
||||
path: path.join(__dirname, '../build'),
|
||||
filename: 'preload.js',
|
||||
},
|
||||
}];
|
||||
|
||||
export default configuration;
|
||||
|
Loading…
Reference in New Issue
Block a user