Spaces:
Runtime error
Runtime error
File size: 6,750 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | /**
* 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");
const MAC_DRAG_STYLE_ID = "omniroute-electron-drag-region-style";
const MAC_DRAG_FALLBACK_ID = "omniroute-electron-drag-region";
const MAC_DRAG_OBSERVER_KEY = "__omnirouteMacDragRegionObserver";
function installMacDragRegion() {
if (process.platform !== "darwin") return;
const attach = () => {
if (!document.head || !document.body) return;
document.getElementById(MAC_DRAG_STYLE_ID)?.remove();
document.getElementById(MAC_DRAG_FALLBACK_ID)?.remove();
const style = document.createElement("style");
style.id = MAC_DRAG_STYLE_ID;
style.textContent = `
header,
.omniroute-electron-drag-region {
app-region: drag;
-webkit-app-region: drag;
user-select: none;
}
header a,
header button,
header input,
header select,
header textarea,
header [role="button"],
header [role="link"],
header [tabindex]:not([tabindex="-1"]) {
app-region: no-drag;
-webkit-app-region: no-drag;
}
.omniroute-electron-drag-region {
position: fixed;
top: 0;
left: 96px;
right: 180px;
height: 46px;
z-index: 9999;
}
`;
const dragRegion = document.createElement("div");
dragRegion.id = MAC_DRAG_FALLBACK_ID;
dragRegion.className = "omniroute-electron-drag-region";
dragRegion.setAttribute("aria-hidden", "true");
document.head.appendChild(style);
document.body.appendChild(dragRegion);
const syncDragFallback = () => {
const hasHeader = Boolean(document.querySelector("header"));
dragRegion.hidden = hasHeader;
if (hasHeader) observer.disconnect();
};
const previousObserver = window[MAC_DRAG_OBSERVER_KEY];
if (previousObserver) previousObserver.disconnect();
const observer = new MutationObserver(syncDragFallback);
observer.observe(document.body, { childList: true, subtree: true });
window[MAC_DRAG_OBSERVER_KEY] = observer;
window.setTimeout(() => observer.disconnect(), 5000);
window.addEventListener("pagehide", () => observer.disconnect(), { once: true });
syncDragFallback();
};
if (document.readyState === "loading") {
window.addEventListener("DOMContentLoaded", attach, { once: true });
} else {
attach();
}
}
installMacDragRegion();
// ββ 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",
"get-autostart-status",
"enable-autostart",
"disable-autostart",
"login:start",
"login:cancel",
"login:status",
],
send: ["window-minimize", "window-maximize", "window-close"],
receive: ["server-status", "port-changed", "update-status", "login: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"),
// ββ Autostart ββββββββββββββββββββββββββββββββββββββββββββ
getAutostartStatus: () => safeInvoke("get-autostart-status"),
enableAutostart: () => safeInvoke("enable-autostart"),
disableAutostart: () => safeInvoke("disable-autostart"),
// ββ 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),
// ββ Web-Cookie Login ββββββββββββββββββββββββββββββββββββββ
startLogin: (providerId, options) => safeInvoke("login:start", providerId, options),
cancelLogin: () => safeInvoke("login:cancel"),
getLoginStatus: () => safeInvoke("login:status"),
onLoginStatus: (callback) => safeOn("login:status", callback),
// ββ Static Properties ββββββββββββββββββββββββββββββββββββ
isElectron: true,
platform: process.platform,
});
|