mirror of
https://github.com/AikidoSec/safe-chain.git
synced 2026-05-26 12:10:49 +00:00
112 lines
3.1 KiB
JavaScript
112 lines
3.1 KiB
JavaScript
import { spawn } from "node:child_process";
|
|
import { existsSync } from "node:fs";
|
|
import { mkdtempSync, readFile } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { ui } from "../../environment/userInteraction.js";
|
|
import { getLoggingLevel, LOGGING_VERBOSE } from "../../config/settings.js";
|
|
|
|
const readFilePromise = promisify(readFile);
|
|
|
|
/**
|
|
* @typedef {Object} RamaProxyInstance
|
|
* @property {import("node:child_process").ChildProcess} process
|
|
* @property {string} proxyAddress
|
|
* @property {string} metaAddress
|
|
* @property {string} caCert
|
|
*/
|
|
|
|
/**
|
|
* @returns {String | null}
|
|
*/
|
|
export function getRamaPath() {
|
|
const executableDir = dirname(process.execPath);
|
|
const ramaPath = join(executableDir, "safechain-proxy");
|
|
|
|
if (existsSync(ramaPath)) {
|
|
return ramaPath;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param {string} ramaPath
|
|
*
|
|
* @returns {import("../registryProxy.js").SafeChainProxy} */
|
|
export function createRamaProxy(ramaPath) {
|
|
const tempDir = mkdtempSync(join(tmpdir(), "safe-chain-proxy-"));
|
|
/** @type {RamaProxyInstance | null} */
|
|
let ramaInstance = null;
|
|
|
|
return {
|
|
startServer: async () => {
|
|
ramaInstance = await startRama(ramaPath, tempDir);
|
|
ui.writeVerbose(
|
|
`Proxy started at address "${ramaInstance.proxyAddress}"`,
|
|
);
|
|
},
|
|
stopServer: async () => {
|
|
if (ramaInstance) {
|
|
ramaInstance.process.kill();
|
|
}
|
|
return Promise.resolve();
|
|
},
|
|
verifyNoMaliciousPackages: () => true,
|
|
hasSuppressedVersions: () => false,
|
|
getServerPort: () => {
|
|
if (!ramaInstance) return null;
|
|
const url = new URL(`http://${ramaInstance.proxyAddress}`);
|
|
return url.port ? parseInt(url.port, 10) : null;
|
|
},
|
|
getCaCert: () => ramaInstance?.caCert ?? null,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {string} ramaPath
|
|
* @param {string} dataFolder
|
|
* @returns {Promise<RamaProxyInstance>}
|
|
*/
|
|
async function startRama(ramaPath, dataFolder) {
|
|
const startTime = Date.now();
|
|
const args = ["--secrets", "memory", "--data", dataFolder];
|
|
const process =
|
|
getLoggingLevel() === LOGGING_VERBOSE
|
|
? spawn(ramaPath, args, {
|
|
stdio: "inherit",
|
|
})
|
|
: spawn(ramaPath, args);
|
|
|
|
// wait for the proxy process to start (poll for proxy.addr.txt file)
|
|
const proxyAddrPath = join(dataFolder, "proxy.addr.txt");
|
|
const maxWaitTime = 60000; // 60 seconds
|
|
const pollInterval = 500; // 500 ms
|
|
|
|
while (!existsSync(proxyAddrPath)) {
|
|
if (Date.now() - startTime > maxWaitTime) {
|
|
throw new Error("Timeout waiting for proxy to start");
|
|
}
|
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
}
|
|
|
|
const elapsedTime = Date.now() - startTime;
|
|
ui.writeVerbose(`Proxy started in ${elapsedTime}ms`);
|
|
|
|
const proxyAddress = await readFilePromise(proxyAddrPath, "utf-8");
|
|
const metaAddress = await readFilePromise(
|
|
join(dataFolder, "meta.addr.txt"),
|
|
"utf-8",
|
|
);
|
|
|
|
const certResponse = await fetch(`http://${metaAddress}/ca`);
|
|
const caCert = await certResponse.text();
|
|
|
|
return {
|
|
process,
|
|
proxyAddress,
|
|
metaAddress,
|
|
caCert,
|
|
};
|
|
}
|