fix: 🐛 electron IPC error handling (#3208)
* fix: 🐛 fixed electron error handling * chore: 🤖 addressed feedback * chore: 🤖 removed error.name, return error as is from main * chore: 🤖 updated error name * chore: 🤖 adds new error on renderer side * chore: 🤖 send error message only * chore: 🤖 adds error fallback
This commit is contained in:
1 parent
7454ecf57a
commit
605b7940e3
2 files changed
+70
-79
No files matched your search
@@ -22,18 +22,13 @@ const log = (method, request = '', response = '', type = 'log') => {
|
||||
module.exports = function handle(command, handler) {
|
||||
ipcMain.handle(command, async function (event, request) {
|
||||
const requestPayload = request;
|
||||
let result;
|
||||
try {
|
||||
result = await handler(requestPayload);
|
||||
const result = await handler(requestPayload);
|
||||
log(command, requestPayload, result);
|
||||
} catch (e) {
|
||||
// If e is an Error instance, we need to extract the message and wrap
|
||||
// it in a POJO before stringifying it. If not an Error, e is assumed to
|
||||
// be already a POJO.
|
||||
const errorMessage = e instanceof Error ? { message: e.message } : e;
|
||||
result = new Error(JSON.stringify(errorMessage));
|
||||
log(command, requestPayload, e, 'error');
|
||||
return { result };
|
||||
} catch (error) {
|
||||
log(command, requestPayload, error, 'error');
|
||||
return { error };
|
||||
}
|
||||
return result;
|
||||
});
|
||||
};
|
||||
@@ -3,40 +3,18 @@
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
const { ipcRenderer, contextBridge } = require('electron');
|
||||
const { ipcRenderer, contextBridge, webFrame } = require('electron');
|
||||
|
||||
/**
|
||||
* Exposing terminal creation to an isolated context (Ember)
|
||||
* More information about contextBridge https://www.electronjs.org/docs/latest/api/context-bridge
|
||||
* usage example: window.terminal.send(data);
|
||||
* Helper function to invoke IPC calls to properly handle errors
|
||||
*/
|
||||
contextBridge.exposeInMainWorld('terminal', {
|
||||
// We could've sent data through our established postMessage pattern
|
||||
// but we don't need a response back so we can make it include it here
|
||||
// to make it simpler. This keeps sending and receiving handlers symmetrical.
|
||||
send: (data, id) => {
|
||||
ipcRenderer.send(`terminalKeystroke-${id}`, data);
|
||||
},
|
||||
receive: (callback, id) => {
|
||||
const incomingDataChannel = `terminalIncomingData-${id}`;
|
||||
const listenerCallback = (_event, value) => callback(value);
|
||||
ipcRenderer.on(incomingDataChannel, listenerCallback);
|
||||
|
||||
// Return a function for the caller to handle cleaning up the listener
|
||||
return () => {
|
||||
return ipcRenderer.removeListener(incomingDataChannel, listenerCallback);
|
||||
};
|
||||
},
|
||||
create: (vars) => {
|
||||
ipcRenderer.send('createTerminal', vars);
|
||||
},
|
||||
remove: (id) => {
|
||||
ipcRenderer.send(`removeTerminal-${id}`);
|
||||
},
|
||||
resize: (size, id) => {
|
||||
ipcRenderer.send(`resize-${id}`, size);
|
||||
},
|
||||
});
|
||||
async function invoke(method, payload) {
|
||||
const { error, result } = await ipcRenderer.invoke(method, payload);
|
||||
if (error) {
|
||||
throw new Error(error.message ?? 'Unknown error');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes `desktop` via Electron ContextBridge to the renderer process. It is grouped into nested objects based on the type of functionality.
|
||||
@@ -44,65 +22,62 @@ contextBridge.exposeInMainWorld('terminal', {
|
||||
*/
|
||||
contextBridge.exposeInMainWorld('desktop', {
|
||||
cluster: {
|
||||
getClusterUrl: () => ipcRenderer.invoke('getClusterUrl'),
|
||||
setClusterUrl: (url) => ipcRenderer.invoke('setClusterUrl', url),
|
||||
resetClusterUrl: () => ipcRenderer.invoke('resetClusterUrl'),
|
||||
getClusterUrl: () => invoke('getClusterUrl'),
|
||||
setClusterUrl: (url) => invoke('setClusterUrl', url),
|
||||
resetClusterUrl: () => invoke('resetClusterUrl'),
|
||||
},
|
||||
|
||||
system: {
|
||||
openExternal: (href) => ipcRenderer.invoke('openExternal', href),
|
||||
cliExists: () => ipcRenderer.invoke('cliExists'),
|
||||
getCliVersion: () => ipcRenderer.invoke('getCliVersion'),
|
||||
checkCommand: (command) => ipcRenderer.invoke('checkCommand', command),
|
||||
checkOS: () => ipcRenderer.invoke('checkOS'),
|
||||
getDesktopVersion: () => ipcRenderer.invoke('getDesktopVersion'),
|
||||
openExternal: (href) => invoke('openExternal', href),
|
||||
cliExists: () => invoke('cliExists'),
|
||||
getCliVersion: () => invoke('getCliVersion'),
|
||||
checkCommand: (command) => invoke('checkCommand', command),
|
||||
checkOS: () => invoke('checkOS'),
|
||||
getDesktopVersion: () => invoke('getDesktopVersion'),
|
||||
},
|
||||
|
||||
session: {
|
||||
connectSession: (params) => ipcRenderer.invoke('connect', params),
|
||||
stopSession: (params) => ipcRenderer.invoke('stop', params),
|
||||
stopAllSessions: () => ipcRenderer.invoke('stopAll'),
|
||||
hasRunningSessions: () => ipcRenderer.invoke('hasRunningSessions'),
|
||||
connectSession: (params) => invoke('connect', params),
|
||||
stopSession: (params) => invoke('stop', params),
|
||||
stopAllSessions: () => invoke('stopAll'),
|
||||
hasRunningSessions: () => invoke('hasRunningSessions'),
|
||||
},
|
||||
|
||||
windowAction: {
|
||||
hasMacOSChrome: () => ipcRenderer.invoke('hasMacOSChrome'),
|
||||
showWindowActions: () => ipcRenderer.invoke('showWindowActions'),
|
||||
minimizeWindow: () => ipcRenderer.invoke('minimizeWindow'),
|
||||
toggleFullscreenWindow: () => ipcRenderer.invoke('toggleFullscreenWindow'),
|
||||
closeWindow: () => ipcRenderer.invoke('closeWindow'),
|
||||
focusWindow: () => ipcRenderer.invoke('focusWindow'),
|
||||
hasMacOSChrome: () => invoke('hasMacOSChrome'),
|
||||
showWindowActions: () => invoke('showWindowActions'),
|
||||
minimizeWindow: () => invoke('minimizeWindow'),
|
||||
toggleFullscreenWindow: () => invoke('toggleFullscreenWindow'),
|
||||
closeWindow: () => invoke('closeWindow'),
|
||||
focusWindow: () => invoke('focusWindow'),
|
||||
},
|
||||
|
||||
daemon: {
|
||||
addTokenToDaemons: (data) => ipcRenderer.invoke('addTokenToDaemons', data),
|
||||
searchCacheDaemon: (request) =>
|
||||
ipcRenderer.invoke('searchCacheDaemon', request),
|
||||
isCacheDaemonRunning: () => ipcRenderer.invoke('isCacheDaemonRunning'),
|
||||
cacheDaemonStatus: () => ipcRenderer.invoke('cacheDaemonStatus'),
|
||||
addTokenToDaemons: (data) => invoke('addTokenToDaemons', data),
|
||||
searchCacheDaemon: (request) => invoke('searchCacheDaemon', request),
|
||||
isCacheDaemonRunning: () => invoke('isCacheDaemonRunning'),
|
||||
cacheDaemonStatus: () => invoke('cacheDaemonStatus'),
|
||||
},
|
||||
|
||||
clientAgent: {
|
||||
getClientAgentSessions: () => ipcRenderer.invoke('getClientAgentSessions'),
|
||||
isClientAgentRunning: () => ipcRenderer.invoke('isClientAgentRunning'),
|
||||
clientAgentStatus: () => ipcRenderer.invoke('clientAgentStatus'),
|
||||
pauseClientAgent: () => ipcRenderer.invoke('pauseClientAgent'),
|
||||
resumeClientAgent: () => ipcRenderer.invoke('resumeClientAgent'),
|
||||
getClientAgentSessions: () => invoke('getClientAgentSessions'),
|
||||
isClientAgentRunning: () => invoke('isClientAgentRunning'),
|
||||
clientAgentStatus: () => invoke('clientAgentStatus'),
|
||||
pauseClientAgent: () => invoke('pauseClientAgent'),
|
||||
resumeClientAgent: () => invoke('resumeClientAgent'),
|
||||
},
|
||||
|
||||
logging: {
|
||||
getLogLevel: () => ipcRenderer.invoke('getLogLevel'),
|
||||
setLogLevel: (logLevel) => ipcRenderer.invoke('setLogLevel', logLevel),
|
||||
getLogPath: () => ipcRenderer.invoke('getLogPath'),
|
||||
getLogLevel: () => invoke('getLogLevel'),
|
||||
setLogLevel: (logLevel) => invoke('setLogLevel', logLevel),
|
||||
getLogPath: () => invoke('getLogPath'),
|
||||
},
|
||||
|
||||
rdp: {
|
||||
getRdpClients: () => ipcRenderer.invoke('getRdpClients'),
|
||||
getPreferredRdpClient: () => ipcRenderer.invoke('getPreferredRdpClient'),
|
||||
setPreferredRdpClient: (client) =>
|
||||
ipcRenderer.invoke('setPreferredRdpClient', client),
|
||||
launchRdpClient: (sessionId) =>
|
||||
ipcRenderer.invoke('launchRdpClient', sessionId),
|
||||
getRdpClients: () => invoke('getRdpClients'),
|
||||
getPreferredRdpClient: () => invoke('getPreferredRdpClient'),
|
||||
setPreferredRdpClient: (client) => invoke('setPreferredRdpClient', client),
|
||||
launchRdpClient: (sessionId) => invoke('launchRdpClient', sessionId),
|
||||
},
|
||||
|
||||
app: {
|
||||
@@ -117,3 +92,24 @@ contextBridge.exposeInMainWorld('desktop', {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('webContentView', {
|
||||
createTerminalView: (params) => {
|
||||
ipcRenderer.send('createTerminalView', {
|
||||
...params,
|
||||
zoomFactor: webFrame.getZoomFactor(),
|
||||
});
|
||||
},
|
||||
destroyTerminalView: () => {
|
||||
ipcRenderer.send('destroyTerminalView');
|
||||
},
|
||||
hideTerminalView: () => {
|
||||
ipcRenderer.send('hideTerminalView');
|
||||
},
|
||||
positionTerminalView: (position) => {
|
||||
ipcRenderer.send('positionTerminalView', {
|
||||
position,
|
||||
zoomFactor: webFrame.getZoomFactor(),
|
||||
});
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user