const { app, BrowserWindow, ipcMain } = require('electron'); const { fork } = require('child_process'); const path = require('path'); const log = require('electron-log'); const sessionId = new Date().toISOString().replace(/[:.]/g, '-'); log.transports.file.resolvePath = () => path.join(app.getPath('userData'), 'logs', `${sessionId}.log`); log.transports.file.level = 'info'; log.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}'; let win; let backend; const net = require('net'); function waitForServer(port, host = '127.0.0.1', timeout = 30000) { return new Promise((resolve, reject) => { const start = Date.now(); const check = () => { const socket = new net.Socket(); socket .once('connect', () => { socket.destroy(); resolve(); }) .once('error', () => { socket.destroy(); if (Date.now() - start > timeout) { reject(new Error('Backend timeout')); } else { setTimeout(check, 200); } }) .connect(port, host); }; check(); }); } function startBackend() { backend = fork(path.join(__dirname, 'server.js'), [], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'], env: { ...process.env, IS_PACKAGED: app.isPackaged ? 'true' : 'false' } }); log.info('Starting backend process...'); backend.stdout.on('data', (data) => { log.info(`[Backend]: ${data.toString().trim()}`); }); backend.stderr.on('data', (data) => { log.error(`[Backend ERROR]: ${data.toString().trim()}`); }); backend.on('exit', (code) => { log.warn(`Backend process exited with code: ${code}`); }); } let splash; function createSplash() { splash = new BrowserWindow({ width: 400, height: 300, frame: false, transparent: false, alwaysOnTop: true, resizable: false, hasShadow: false, backgroundColor: '#00000000' }); splash.loadFile('loading.html'); } function createWindow() { win = new BrowserWindow({ width: 1200, height: 800, frame: false, titleBarStyle: "hidden", webPreferences: { preload: path.join(__dirname, "preload.js"), nodeIntegration: false, contextIsolation: true } }); win.setMenu(null); win.maximize(); win.loadURL('http://localhost:54322'); win.on('closed', () => { win = null; }); } ipcMain.on("win:minimize", () => win.minimize()); ipcMain.on("win:maximize", () => { if (win.isMaximized()) { win.unmaximize(); } else { win.maximize(); } }); ipcMain.on("win:close", () => win.close()); process.on('uncaughtException', (err) => { log.error('Critical unhandled error in Main:', err); }); app.whenReady().then(async () => { startBackend(); createSplash(); try { await waitForServer(54322); createWindow(); splash.close(); } catch (e) { splash.close(); log.error(e); app.quit(); } }); app.on('window-all-closed', () => { log.info('Closing all windows...'); if (backend) { backend.kill(); log.info('Backend process terminated.'); } if (process.platform !== 'darwin') { app.quit(); } });