feat: implement single instance lock to ensure data integrity and resource efficiency
This commit is contained in:
@@ -358,60 +358,82 @@ function sendToRenderer(channel: string, ...args: unknown[]): void {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 7. App Lifecycle ───────────────────────────────────────────
|
||||
// ─── 7. Single Instance Lock & App Lifecycle ────────────────────
|
||||
// Prevent multiple instances to protect local database integrity
|
||||
// (IndexedDB/PouchDB) and avoid resource contention.
|
||||
|
||||
app.whenReady().then(() => {
|
||||
// Register the custom protocol before creating the window
|
||||
registerAppProtocol();
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
|
||||
// Setup CORS bypass for API calls
|
||||
setupCorsBypass();
|
||||
|
||||
// Setup IPC handlers
|
||||
setupPrinterIPC();
|
||||
setupAutoUpdaterIPC();
|
||||
setupAutoUpdaterEvents();
|
||||
|
||||
// Create the main window
|
||||
createWindow();
|
||||
|
||||
// Check for updates on startup (production only)
|
||||
if (!IS_DEV) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await autoUpdater.checkForUpdatesAndNotify();
|
||||
} catch (err) {
|
||||
// Gracefully handle offline or network errors
|
||||
console.error('[AutoUpdater] Startup check failed (possibly offline):', err);
|
||||
if (!gotTheLock) {
|
||||
// Another instance is already running — terminate immediately.
|
||||
app.quit();
|
||||
} else {
|
||||
// ── Handle second-instance launch attempts ──────────────────
|
||||
// If a user tries to open a second instance, restore and
|
||||
// focus the existing window instead.
|
||||
app.on('second-instance', () => {
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore();
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// macOS: re-create window when dock icon is clicked
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
mainWindow.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Quit when all windows are closed (except macOS)
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
// ── Primary initialization ──────────────────────────────────
|
||||
app.whenReady().then(() => {
|
||||
// Register the custom protocol before creating the window
|
||||
registerAppProtocol();
|
||||
|
||||
// Security: prevent navigation to unexpected URLs
|
||||
app.on('web-contents-created', (_event, contents) => {
|
||||
contents.on('will-navigate', (event, url) => {
|
||||
// Allow navigation within the app protocol and dev server
|
||||
if (
|
||||
url.startsWith('app://') ||
|
||||
(IS_DEV && url.startsWith(DEV_SERVER_URL))
|
||||
) {
|
||||
return;
|
||||
// Setup CORS bypass for API calls
|
||||
setupCorsBypass();
|
||||
|
||||
// Setup IPC handlers
|
||||
setupPrinterIPC();
|
||||
setupAutoUpdaterIPC();
|
||||
setupAutoUpdaterEvents();
|
||||
|
||||
// Create the main window
|
||||
createWindow();
|
||||
|
||||
// Check for updates on startup (production only)
|
||||
if (!IS_DEV) {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await autoUpdater.checkForUpdatesAndNotify();
|
||||
} catch (err) {
|
||||
// Gracefully handle offline or network errors
|
||||
console.error('[AutoUpdater] Startup check failed (possibly offline):', err);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
event.preventDefault();
|
||||
|
||||
// macOS: re-create window when dock icon is clicked
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Quit when all windows are closed (except macOS)
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
// Security: prevent navigation to unexpected URLs
|
||||
app.on('web-contents-created', (_event, contents) => {
|
||||
contents.on('will-navigate', (event, url) => {
|
||||
// Allow navigation within the app protocol and dev server
|
||||
if (
|
||||
url.startsWith('app://') ||
|
||||
(IS_DEV && url.startsWith(DEV_SERVER_URL))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user