Spaces:
Paused
Paused
| /** | |
| * 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, | |
| }); | |