82 lines
3.5 KiB
JavaScript
82 lines
3.5 KiB
JavaScript
/**
|
|
* OmniRoute Electron Desktop App - Preload Script
|
|
*
|
|
* Secure bridge between renderer (Next.js) and main process (Electron).
|
|
* Uses contextIsolation: true for maximum security.
|
|
*
|
|
* Code Review Fixes Applied:
|
|
* #6 Listener accumulation — return disposer functions instead of using removeAllListeners
|
|
* #16 Simplified channel validation — generic wrapper reduces boilerplate
|
|
*/
|
|
|
|
const { contextBridge, ipcRenderer } = require("electron");
|
|
|
|
// ── Channel Whitelist ──────────────────────────────────────
|
|
const VALID_CHANNELS = {
|
|
invoke: [
|
|
"get-app-info",
|
|
"open-external",
|
|
"get-data-dir",
|
|
"restart-server",
|
|
"check-for-updates",
|
|
"download-update",
|
|
"install-update",
|
|
"get-app-version",
|
|
],
|
|
send: ["window-minimize", "window-maximize", "window-close"],
|
|
receive: ["server-status", "port-changed", "update-status"],
|
|
};
|
|
|
|
// ── Fix #16: Generic IPC wrappers ──────────────────────────
|
|
function safeInvoke(channel, ...args) {
|
|
if (!VALID_CHANNELS.invoke.includes(channel)) {
|
|
return Promise.reject(new Error(`Blocked IPC invoke: ${channel}`));
|
|
}
|
|
return ipcRenderer.invoke(channel, ...args);
|
|
}
|
|
|
|
function safeSend(channel, ...args) {
|
|
if (VALID_CHANNELS.send.includes(channel)) {
|
|
ipcRenderer.send(channel, ...args);
|
|
}
|
|
}
|
|
|
|
// Fix #6: Return disposer function for proper listener cleanup
|
|
function safeOn(channel, callback) {
|
|
if (!VALID_CHANNELS.receive.includes(channel)) return () => {};
|
|
const handler = (_event, data) => callback(data);
|
|
ipcRenderer.on(channel, handler);
|
|
// Return a disposer — caller removes only THIS specific listener
|
|
return () => ipcRenderer.removeListener(channel, handler);
|
|
}
|
|
|
|
// ── Expose API to Renderer ─────────────────────────────────
|
|
contextBridge.exposeInMainWorld("electronAPI", {
|
|
// ── Invoke (async, returns Promise) ──────────────────────
|
|
getAppInfo: () => safeInvoke("get-app-info"),
|
|
openExternal: (url) => safeInvoke("open-external", url),
|
|
getDataDir: () => safeInvoke("get-data-dir"),
|
|
restartServer: () => safeInvoke("restart-server"),
|
|
getAppVersion: () => safeInvoke("get-app-version"),
|
|
|
|
// ── Auto-Update ──────────────────────────────────────────
|
|
checkForUpdates: () => safeInvoke("check-for-updates"),
|
|
downloadUpdate: () => safeInvoke("download-update"),
|
|
installUpdate: () => safeInvoke("install-update"),
|
|
|
|
// ── Send (fire-and-forget) ───────────────────────────────
|
|
minimizeWindow: () => safeSend("window-minimize"),
|
|
maximizeWindow: () => safeSend("window-maximize"),
|
|
closeWindow: () => safeSend("window-close"),
|
|
|
|
// ── Receive (event listeners) ────────────────────────────
|
|
// Fix #6: Returns a disposer function for precise cleanup
|
|
onServerStatus: (callback) => safeOn("server-status", callback),
|
|
onPortChanged: (callback) => safeOn("port-changed", callback),
|
|
onUpdateStatus: (callback) => safeOn("update-status", callback),
|
|
|
|
// ── Static Properties ────────────────────────────────────
|
|
isElectron: true,
|
|
platform: process.platform,
|
|
});
|