const { app, BrowserWindow, ipcMain } = require('electron') const path = require('path') const fs = require('fs') const isDev = process.env.NODE_ENV !== 'production' const devUrl = 'http://localhost:5173' const API_URL_KEY = 'runtime_api_url' const DEFAULT_API_URL = process.env.VITE_API_URL || 'http://localhost:4000/api' let mainWindow function normalizeApiUrl(value) { if (!value || typeof value !== 'string') return null const trimmed = value.trim().replace(/\/+$/, '') if (!trimmed) return null if (!/^https?:\/\//i.test(trimmed)) return null return /\/api$/i.test(trimmed) ? trimmed : `${trimmed}/api` } function getStoragePath() { return path.join(app.getPath('userData'), 'storage.json') } function readStorage() { try { const p = getStoragePath() if (!fs.existsSync(p)) return {} const raw = fs.readFileSync(p, 'utf8') || '{}' return JSON.parse(raw) } catch (e) { return {} } } function writeStorage(obj) { try { fs.mkdirSync(path.dirname(getStoragePath()), { recursive: true }) fs.writeFileSync(getStoragePath(), JSON.stringify(obj, null, 2), 'utf8') return true } catch (e) { return false } } ipcMain.handle('storage-get', (event, key) => { const s = readStorage() if (!key) return s if (!(key in s)) return null return { value: s[key] } }) ipcMain.handle('storage-set', (event, key, value) => { const s = readStorage() s[key] = value writeStorage(s) return true }) ipcMain.handle('storage-remove', (event, key) => { const s = readStorage() delete s[key] writeStorage(s) return true }) ipcMain.handle('get-api-url', () => { const s = readStorage() return normalizeApiUrl(s[API_URL_KEY]) || normalizeApiUrl(DEFAULT_API_URL) || 'http://localhost:4000/api' }) ipcMain.handle('set-api-url', (event, value) => { const next = normalizeApiUrl(value) if (!next) throw new Error('API URL must be a valid http(s) URL') const s = readStorage() s[API_URL_KEY] = next writeStorage(s) return next }) function createWindow() { mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: path.join(__dirname, 'preload.cjs'), contextIsolation: true, nodeIntegration: false } }) if (isDev) { mainWindow.loadURL(devUrl) mainWindow.webContents.openDevTools() } else { mainWindow.loadFile(path.join(__dirname, '..', 'dist', 'index.html')) } } app.whenReady().then(createWindow) app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() }) app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() })