mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-04-28 06:19:46 +00:00
feat(electron): add auto-update functionality with electron-updater
This commit is contained in:
parent
f96ee44213
commit
3342d5b931
3 changed files with 144 additions and 3 deletions
118
electron/main.js
118
electron/main.js
|
|
@ -26,10 +26,13 @@ const {
|
|||
nativeImage,
|
||||
shell,
|
||||
session,
|
||||
dialog,
|
||||
Notification,
|
||||
} = require("electron");
|
||||
const path = require("path");
|
||||
const { spawn } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const { autoUpdater } = require("electron-updater");
|
||||
|
||||
// ── Single Instance Lock ───────────────────────────────────
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
|
|
@ -62,6 +65,11 @@ let serverPort = 20128;
|
|||
|
||||
const getServerUrl = () => `http://localhost:${serverPort}`;
|
||||
|
||||
// ── Auto-Updater Configuration ──────────────────────────────
|
||||
autoUpdater.autoDownload = false;
|
||||
autoUpdater.autoInstallOnAppQuit = true;
|
||||
autoUpdater.logger = console;
|
||||
|
||||
// ── Helper: Send IPC event to renderer (#5) ────────────────
|
||||
function sendToRenderer(channel, data) {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
|
|
@ -103,6 +111,85 @@ async function waitForServerExit(proc, timeoutMs = 5000) {
|
|||
]);
|
||||
}
|
||||
|
||||
// ── Auto-Updater Event Handlers ─────────────────────────────
|
||||
function setupAutoUpdater() {
|
||||
autoUpdater.on("checking-for-update", () => {
|
||||
sendToRenderer("update-status", { status: "checking" });
|
||||
console.log("[Electron] Checking for updates...");
|
||||
});
|
||||
|
||||
autoUpdater.on("update-available", (info) => {
|
||||
sendToRenderer("update-status", { status: "available", version: info.version });
|
||||
console.log("[Electron] Update available:", info.version);
|
||||
});
|
||||
|
||||
autoUpdater.on("update-not-available", (info) => {
|
||||
sendToRenderer("update-status", { status: "not-available", version: info.version });
|
||||
console.log("[Electron] No update available");
|
||||
});
|
||||
|
||||
autoUpdater.on("download-progress", (progress) => {
|
||||
sendToRenderer("update-status", {
|
||||
status: "downloading",
|
||||
percent: Math.round(progress.percent),
|
||||
transferred: progress.transferred,
|
||||
total: progress.total,
|
||||
});
|
||||
});
|
||||
|
||||
autoUpdater.on("update-downloaded", (info) => {
|
||||
sendToRenderer("update-status", { status: "downloaded", version: info.version });
|
||||
console.log("[Electron] Update downloaded:", info.version);
|
||||
|
||||
if (Notification.isSupported()) {
|
||||
const notification = new Notification({
|
||||
title: "OmniRoute Update Ready",
|
||||
body: `Version ${info.version} is ready to install. Click to restart.`,
|
||||
});
|
||||
notification.on("click", () => {
|
||||
autoUpdater.quitAndInstall();
|
||||
});
|
||||
notification.show();
|
||||
}
|
||||
});
|
||||
|
||||
autoUpdater.on("error", (error) => {
|
||||
sendToRenderer("update-status", { status: "error", message: error.message });
|
||||
console.error("[Electron] Update error:", error);
|
||||
});
|
||||
}
|
||||
|
||||
async function checkForUpdates(silent = false) {
|
||||
if (isDev) {
|
||||
console.log("[Electron] Dev mode — skipping auto-update");
|
||||
if (!silent) {
|
||||
sendToRenderer("update-status", { status: "error", message: "Updates disabled in dev mode" });
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await autoUpdater.checkForUpdates();
|
||||
} catch (error) {
|
||||
console.error("[Electron] Check for updates failed:", error);
|
||||
if (!silent) {
|
||||
sendToRenderer("update-status", { status: "error", message: error.message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadUpdate() {
|
||||
try {
|
||||
await autoUpdater.downloadUpdate();
|
||||
} catch (error) {
|
||||
console.error("[Electron] Download update failed:", error);
|
||||
sendToRenderer("update-status", { status: "error", message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
function installUpdate() {
|
||||
autoUpdater.quitAndInstall();
|
||||
}
|
||||
|
||||
// ── Content Security Policy (#15) ──────────────────────────
|
||||
function setupContentSecurityPolicy() {
|
||||
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
||||
|
|
@ -236,6 +323,11 @@ function createTray() {
|
|||
],
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Check for Updates",
|
||||
click: () => checkForUpdates(false),
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Quit",
|
||||
click: () => {
|
||||
|
|
@ -391,6 +483,24 @@ function setupIpcHandlers() {
|
|||
});
|
||||
|
||||
ipcMain.on("window-close", () => mainWindow?.close());
|
||||
|
||||
// Auto-update IPC handlers
|
||||
ipcMain.handle("check-for-updates", async () => {
|
||||
await checkForUpdates(false);
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
ipcMain.handle("download-update", async () => {
|
||||
await downloadUpdate();
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
ipcMain.handle("install-update", () => {
|
||||
installUpdate();
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
ipcMain.handle("get-app-version", () => app.getVersion());
|
||||
}
|
||||
|
||||
// ── App Lifecycle ──────────────────────────────────────────
|
||||
|
|
@ -407,6 +517,14 @@ app.whenReady().then(async () => {
|
|||
createWindow();
|
||||
createTray();
|
||||
setupIpcHandlers();
|
||||
setupAutoUpdater();
|
||||
|
||||
// Check for updates after a short delay (don't block startup)
|
||||
if (!isDev) {
|
||||
setTimeout(() => {
|
||||
checkForUpdates(true);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// macOS: recreate window when dock icon clicked
|
||||
app.on("activate", () => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@
|
|||
"build:linux": "electron-builder --linux",
|
||||
"pack": "electron-builder --dir"
|
||||
},
|
||||
"dependencies": {},
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^40.6.1",
|
||||
"electron-builder": "^25.1.8"
|
||||
|
|
@ -28,6 +30,11 @@
|
|||
"output": "dist-electron",
|
||||
"buildResources": "assets"
|
||||
},
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"owner": "diegosouzapw",
|
||||
"repo": "OmniRoute"
|
||||
},
|
||||
"files": [
|
||||
"main.js",
|
||||
"preload.js",
|
||||
|
|
|
|||
|
|
@ -13,9 +13,18 @@ const { contextBridge, ipcRenderer } = require("electron");
|
|||
|
||||
// ── Channel Whitelist ──────────────────────────────────────
|
||||
const VALID_CHANNELS = {
|
||||
invoke: ["get-app-info", "open-external", "get-data-dir", "restart-server"],
|
||||
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"],
|
||||
receive: ["server-status", "port-changed", "update-status"],
|
||||
};
|
||||
|
||||
// ── Fix #16: Generic IPC wrappers ──────────────────────────
|
||||
|
|
@ -48,6 +57,12 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
|||
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"),
|
||||
|
|
@ -58,6 +73,7 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
|||
// 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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue