fix: port conflicts and API proxy for Electron dev mode

- Add Vite proxy: /api/* requests from renderer (5173) → Express (3000)
- Add dynamic port finding: if 3000 is taken, auto-pick next available
- Import net module for port checking
- Use dynamic expressPort in production URL loading
This commit is contained in:
2026-06-02 04:18:34 -05:00
parent e7520b28f9
commit fbdbd7e05c
2 changed files with 43 additions and 7 deletions
+7
View File
@@ -67,6 +67,13 @@ export default defineConfig({
server: { server: {
hmr: process.env.DISABLE_HMR !== 'true', hmr: process.env.DISABLE_HMR !== 'true',
watch: process.env.DISABLE_HMR === 'true' ? null : {}, watch: process.env.DISABLE_HMR === 'true' ? null : {},
// Proxy API calls to the embedded Express server
proxy: {
'/api': {
target: 'http://127.0.0.1:3000',
changeOrigin: true,
},
},
}, },
}, },
}); });
+36 -7
View File
@@ -10,13 +10,36 @@
import { app, BrowserWindow, ipcMain, dialog } from 'electron'; import { app, BrowserWindow, ipcMain, dialog } from 'electron';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import net from 'net';
// Signal to server.ts / renderQueue.ts that we're in Electron // Signal to server.ts / renderQueue.ts that we're in Electron
process.env.BRADLY_ELECTRON = 'true'; process.env.BRADLY_ELECTRON = 'true';
const EXPRESS_PORT = 3000; const PREFERRED_PORT = 3000;
let expressPort = PREFERRED_PORT;
let mainWindow: BrowserWindow | null = null; let mainWindow: BrowserWindow | null = null;
// ═══ Port Utilities ═══
/** Find an available port, starting from the preferred one */
function getAvailablePort(startPort: number): Promise<number> {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.listen(startPort, '127.0.0.1', () => {
const port = (server.address() as net.AddressInfo).port;
server.close(() => resolve(port));
});
server.on('error', () => {
// Port in use, try the next one
if (startPort < 65535) {
resolve(getAvailablePort(startPort + 1));
} else {
reject(new Error('No available ports found'));
}
});
});
}
// ═══ Path Setup for Electron ═══ // ═══ Path Setup for Electron ═══
function setupPaths() { function setupPaths() {
@@ -72,7 +95,10 @@ function setupPaths() {
// ═══ Express Server ═══ // ═══ Express Server ═══
async function startExpressServer(): Promise<void> { async function startExpressServer(): Promise<number> {
// Find an available port
expressPort = await getAvailablePort(PREFERRED_PORT);
const { createExpressApp } = await import('../../server'); const { createExpressApp } = await import('../../server');
const expressApp = await createExpressApp(); const expressApp = await createExpressApp();
@@ -96,10 +122,13 @@ async function startExpressServer(): Promise<void> {
} }
} }
return new Promise<void>((resolve, reject) => { return new Promise<number>((resolve, reject) => {
const server = expressApp.listen(EXPRESS_PORT, '127.0.0.1', () => { const server = expressApp.listen(expressPort, '127.0.0.1', () => {
console.log(`🚀 Express server on http://127.0.0.1:${EXPRESS_PORT}`); console.log(`🚀 Express server on http://127.0.0.1:${expressPort}`);
resolve(); if (expressPort !== PREFERRED_PORT) {
console.warn(`⚠️ Port ${PREFERRED_PORT} was in use, using ${expressPort} instead`);
}
resolve(expressPort);
}); });
server.on('error', reject); server.on('error', reject);
}); });
@@ -140,7 +169,7 @@ function createWindow() {
} else { } else {
// In production, load from the embedded Express server // In production, load from the embedded Express server
// which serves the built renderer assets // which serves the built renderer assets
mainWindow.loadURL(`http://127.0.0.1:${EXPRESS_PORT}`); mainWindow.loadURL(`http://127.0.0.1:${expressPort}`);
} }
mainWindow.on('closed', () => { mainWindow.on('closed', () => {