computer-auditor / dist-electron /pythonBackend.js
algorembrant's picture
Upload 28 files
b4143a2 verified
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPythonBaseUrl = getPythonBaseUrl;
exports.startPythonBackend = startPythonBackend;
exports.stopPythonBackend = stopPythonBackend;
exports.pyGet = pyGet;
exports.pyPost = pyPost;
/**
* Spawns the FastAPI Python backend and exposes HTTP helpers for the main process.
* Python handles hot paths (filesystem sizes, processes, drives, system, network, env).
*/
const node_child_process_1 = require("node:child_process");
const net = __importStar(require("node:net"));
const path = __importStar(require("node:path"));
let child = null;
let baseUrl = '';
function getPythonBaseUrl() {
return baseUrl;
}
function getFreePort() {
return new Promise((resolve, reject) => {
const s = net.createServer();
s.listen(0, '127.0.0.1', () => {
const a = s.address();
const p = typeof a === 'object' && a ? a.port : 0;
s.close(() => resolve(p));
});
s.on('error', reject);
});
}
async function waitForHealth(port, timeoutMs) {
const url = `http://127.0.0.1:${port}/health`;
const t0 = Date.now();
while (Date.now() - t0 < timeoutMs) {
try {
const r = await fetch(url);
if (r.ok)
return;
}
catch {
/* retry */
}
await new Promise((r) => setTimeout(r, 120));
}
throw new Error(`Python backend did not respond at ${url}`);
}
async function startPythonBackend() {
const port = await getFreePort();
const backendDir = path.join(__dirname, '..', 'backend');
const py = process.env.PYTHON_PATH ?? (process.platform === 'win32' ? 'python' : 'python3');
child = (0, node_child_process_1.spawn)(py, ['-m', 'uvicorn', 'main:app', '--host', '127.0.0.1', '--port', String(port), '--log-level', 'warning'], {
cwd: backendDir,
env: { ...process.env, AUDITOR_PY_PORT: String(port) },
stdio: ['ignore', 'pipe', 'pipe'],
windowsHide: true,
});
let stderrBuf = '';
child.stderr?.on('data', (d) => {
stderrBuf += d.toString();
if (stderrBuf.length > 8000)
stderrBuf = stderrBuf.slice(-4000);
});
await new Promise((resolve, reject) => {
const fail = (err) => {
try {
child?.kill();
}
catch {
/* ignore */
}
reject(err);
};
const t = setTimeout(() => {
fail(new Error(`Python backend startup timed out. Install: cd backend && pip install -r requirements.txt\n${stderrBuf}`));
}, 25_000);
child.once('error', (err) => {
clearTimeout(t);
fail(err instanceof Error ? err : new Error(String(err)));
});
waitForHealth(port, 24_000)
.then(() => {
clearTimeout(t);
resolve();
})
.catch((e) => {
clearTimeout(t);
fail(e instanceof Error ? e : new Error(String(e)));
});
});
baseUrl = `http://127.0.0.1:${port}`;
}
function stopPythonBackend() {
if (child) {
try {
child.kill();
}
catch {
/* ignore */
}
child = null;
}
baseUrl = '';
}
async function pyGet(pathname) {
const r = await fetch(`${baseUrl}${pathname}`);
if (!r.ok) {
const t = await r.text();
throw new Error(t || r.statusText);
}
return r.json();
}
async function pyPost(pathname, body) {
const r = await fetch(`${baseUrl}${pathname}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (!r.ok) {
const t = await r.text();
throw new Error(t || r.statusText);
}
return r.json();
}