dev-server.js
Rama main · réplica de lectura del repositorio original.
/**
* Standalone dev server — runs the Express + SQLite backend WITHOUT Electron.
* Mocks only the Electron APIs used by db.ts and server.ts.
* Usage: node dev-server.js
*/
// Load .env before anything else
const fs = require('fs');
const envPath = require('path').join(__dirname, '.env');
if (fs.existsSync(envPath)) {
for (const line of fs.readFileSync(envPath, 'utf8').split('\n')) {
const match = line.match(/^\s*([^#=\s]+)\s*=\s*(.*)$/);
if (!match) continue;
let value = match[2].trim();
if (value.startsWith('"') && value.endsWith('"')) {
try { value = JSON.parse(value); } catch { value = value.slice(1, -1); }
} else if (value.startsWith("'") && value.endsWith("'")) {
value = value.slice(1, -1);
} else {
value = value.replace(/\s+#.*$/, '').trim();
}
process.env[match[1]] = value;
}
console.log('[DevServer] Loaded .env');
}
const path = require('path');
const os = require('os');
const devUserDataPath = process.env.FLO_DEV_USER_DATA || __dirname;
// ── Mock Electron's `app` module ──────────────────────────────────────────────
const mockApp = {
isPackaged: Boolean(process.env.FLO_DEV_USER_DATA),
getPath: (name) => {
if (name === 'userData') return devUserDataPath;
if (name === 'documents') return os.homedir();
return os.tmpdir();
},
getVersion: () => require('./package.json').version,
getName: () => 'Flo (dev)',
};
require('module').Module._resolveFilename = (function (original) {
return function (request, ...args) {
if (request === 'electron') {
return __filename; // will be intercepted below
}
return original.call(this, request, ...args);
};
})(require('module').Module._resolveFilename);
// Intercept `require('electron')` before any dist file loads it
const Module = require('module');
const originalLoad = Module._load;
Module._load = function (request, parent, isMain) {
if (request === 'electron') {
return {
app: mockApp,
// Stub anything else that might be imported at module level
BrowserWindow: class {},
ipcMain: { handle: () => {}, on: () => {} },
dialog: {},
shell: {},
Menu: { buildFromTemplate: () => ({}), setApplicationMenu: () => {} },
Tray: class {},
nativeImage: { createFromPath: () => ({ resize: () => ({}) }) },
};
}
return originalLoad.call(this, request, parent, isMain);
};
// ── Now load and start the compiled backend ───────────────────────────────────
const { initDatabase, closeDatabase, beginDatabaseShutdown, waitForDatabaseRequests } = require('./dist/main/db');
const { createExitCodeAwareShutdown, waitForHttpShutdownWork, isShutdownTimeout } = require('./dist/main/shutdown');
const { startServer, stopServer, getServerPort } = require('./dist/main/server');
const { startKdsServer, stopKdsServer, getKdsPort } = require('./dist/main/kds-server');
const { startServerApp, stopServerApp, getServerAppPort } = require('./dist/main/server-app');
const { shutdown: shutdownWhatsApp, requestShutdown: requestWhatsAppShutdown } = require('./dist/main/services/whatsapp');
const { startStandaloneServers } = require('./dist/main/standalone-startup');
let exitRequested = false;
let shutdownRequested = false;
const requestShutdown = createExitCodeAwareShutdown(async () => {
let cleanupFailed = false;
let databaseBlocked = false;
try { await stopServerApp(); } catch (err) { console.error('[DevServer] Server App shutdown failed:', err); cleanupFailed = true; databaseBlocked = true; if (isShutdownTimeout(err)) throw err; }
try { await stopServer(); } catch (err) { console.error('[DevServer] Main server shutdown failed:', err); cleanupFailed = true; databaseBlocked = true; if (isShutdownTimeout(err)) throw err; }
try { await stopKdsServer(); } catch (err) { console.error('[DevServer] KDS server shutdown failed:', err); cleanupFailed = true; databaseBlocked = true; if (isShutdownTimeout(err)) throw err; }
try { await shutdownWhatsApp(); } catch (err) { console.error('[DevServer] WhatsApp shutdown failed:', err); cleanupFailed = true; databaseBlocked = true; if (isShutdownTimeout(err)) throw err; }
try { await waitForHttpShutdownWork(); } catch (err) { console.error('[DevServer] HTTP handler cleanup failed:', err); cleanupFailed = true; databaseBlocked = true; if (isShutdownTimeout(err)) throw err; }
try { beginDatabaseShutdown(); await waitForDatabaseRequests(); } catch (err) { console.error('[DevServer] Database request drain failed:', err); cleanupFailed = true; databaseBlocked = true; if (isShutdownTimeout(err)) throw err; }
if (!databaseBlocked) {
try { closeDatabase(); } catch (err) { console.error('[DevServer] Database shutdown failed:', err); cleanupFailed = true; }
}
Module._load = originalLoad;
return cleanupFailed ? 1 : 0;
}, {
onShutdownRequested: requestWhatsAppShutdown,
onFatalTimeout: () => process.exit(1),
});
async function shutdown(exitCode = 0) {
shutdownRequested = true;
const finalExitCode = await requestShutdown(exitCode);
if (!exitRequested) {
exitRequested = true;
process.exit(finalExitCode);
}
}
process.once('SIGINT', () => void shutdown(0));
process.once('SIGTERM', () => void shutdown(0));
if (process.platform === 'win32') {
process.stdin.setEncoding('utf8');
process.stdin.on('data', (chunk) => {
if (chunk.includes('SIGTERM') || chunk.includes('SIGINT') || chunk.includes('SHUTDOWN')) {
void shutdown(0);
}
});
}
process.on('uncaughtException', (err) => {
console.error('[DevServer] Uncaught exception:', err);
void shutdown(1);
});
process.on('unhandledRejection', (err) => {
console.error('[DevServer] Unhandled rejection:', err);
void shutdown(1);
});
(async () => {
try {
console.log('[DevServer] Initializing database...');
console.log('[DevServer] Starting Express, KDS, and Server App servers...');
await startStandaloneServers({
initializeDatabase: initDatabase,
startServer,
startKdsServer,
startServerApp,
isShutdownRequested: () => shutdownRequested,
});
console.log(`[DevServer] ✅ Main API running on http://localhost:${getServerPort()}`);
console.log(`[DevServer] ✅ KDS Server running on http://localhost:${getKdsPort()}`);
console.log(`[DevServer] ✅ Server App running on http://localhost:${getServerAppPort()}`);
console.log('[DevServer] Frontend dev server: http://localhost:3000');
console.log(`[DevServer] API health: http://localhost:${getServerPort()}/api/health`);
} catch (err) {
console.error('[DevServer] Failed to start:', err);
const code = err && (err.code === 'ERR_SHUTDOWN_ABORTED' || err.code === 'ABORT_ERR' || err.name === 'AbortError') ? 0 : 1;
await shutdown(code);
}
})();