cleanup processControl

This commit is contained in:
ThatBen 2025-04-14 13:56:55 +02:00
parent 7661d829cb
commit 5fcb55015a
No known key found for this signature in database
GPG Key ID: E020A7DDCD52E1AB
7 changed files with 100 additions and 75 deletions

View File

@ -15,6 +15,7 @@ export const mockConfigService = {
get: vi.fn(), get: vi.fn(),
saveConfig: vi.fn(), saveConfig: vi.fn(),
loadConfig: vi.fn(), loadConfig: vi.fn(),
writeCodexConfigFile: vi.fn(),
}; };
export const mockFsService = { export const mockFsService = {
@ -32,6 +33,7 @@ export const mockFsService = {
export const mockShellService = { export const mockShellService = {
run: vi.fn(), run: vi.fn(),
spawnDetachedProcess: vi.fn(),
}; };
export const mockOsService = { export const mockOsService = {
@ -39,4 +41,10 @@ export const mockOsService = {
isDarwin: vi.fn(), isDarwin: vi.fn(),
isLinux: vi.fn(), isLinux: vi.fn(),
getWorkingDir: vi.fn(), getWorkingDir: vi.fn(),
listProcesses: vi.fn(),
};
export const mockCodexGlobals = {
getPublicIp: vi.fn(),
getTestnetSPRs: vi.fn(),
}; };

View File

@ -1,95 +1,49 @@
import fs from "fs";
import { spawn, exec } from "child_process";
import psList from 'ps-list';
export class ProcessControl { export class ProcessControl {
constructor(configService, shellService, osService, fsService) { constructor(configService, shellService, osService, fsService, codexGlobals) {
this.configService = configService; this.configService = configService;
this.config = configService.get(); this.config = configService.get();
this.shell = shellService; this.shell = shellService;
this.os = osService; this.os = osService;
this.fs = fsService; this.fs = fsService;
this.codexGlobals;
} }
getPublicIp = async () => { getCodexProcesses = async () => {
const processes = await this.os.listProcesses();
if (this.os.isWindows()) { if (this.os.isWindows()) {
const result = await this.shell.run( return processes.filter((p) => p.name === "codex.exe");
"for /f \"delims=\" %a in ('curl -s --ssl-reqd ip.codex.storage') do @echo %a",
);
return result.trim();
} else { } else {
return await this.shell.run("curl -s https://ip.codex.storage"); return processes.filter((p) => p.name === "codex");
} }
}; };
detectThing = async () => { getNumberOfCodexProcesses = async () => {
console.log("detecting..."); return (await this.getCodexProcesses()).length;
};
const processes = await psList(); stopCodexProcess = async () => {
const codexProcesses = processes.filter((p) => p.name === "codex.exe"); const processes = await this.getCodexProcesses();
if (codexProcesses.length > 0) { if (processes.length < 1) throw new Error("No codex process found");
console.log("Codex is already running.");
codexProcesses.forEach((p) => {
console.log(`PID: ${JSON.stringify(p)}`);
});
console.log("Stopping codex...");
await this.stopThing(codexProcesses[0].pid);
await this.detectThing();
} else {
console.log("Codex is not running.");
}
}
stopThing = async (pid) => {
console.log("stopping process...");
const pid = processes[0].pid;
process.kill(pid, "SIGINT"); process.kill(pid, "SIGINT");
await new Promise((resolve) => setTimeout(resolve, 2000)); await new Promise((resolve) => setTimeout(resolve, 2000));
} };
doThing = async () => { startCodexProcess = async () => {
if (this.config.dataDir.length < 1) this.saveCodexConfigFile();
throw new Error("Missing config: dataDir"); this.startCodex();
if (this.config.logsDir.length < 1) };
throw new Error("Missing config: logsDir");
console.log("start a codex detached"); saveCodexConfigFile = async () => {
const publicIp = await this.codexGlobals.getPublicIp();
const bootstrapNodes = await this.codexGlobals.getTestnetSPRs();
this.configService.writeCodexConfigFile(publicIp, bootstrapNodes);
};
startCodex = async () => {
const executable = this.config.codexExe; const executable = this.config.codexExe;
const args = [`--config-file=${this.config.codexConfigFilePath}`]; const args = [`--config-file=${this.config.codexConfigFilePath}`];
const bootstrapNodes = [ await this.shell.spawnDetachedProcess(executable, args);
"spr:CiUIAhIhAiJvIcA_ZwPZ9ugVKDbmqwhJZaig5zKyLiuaicRcCGqLEgIDARo8CicAJQgCEiECIm8hwD9nA9n26BUoNuarCEllqKDnMrIuK5qJxFwIaosQ3d6esAYaCwoJBJ_f8zKRAnU6KkYwRAIgM0MvWNJL296kJ9gWvfatfmVvT-A7O2s8Mxp8l9c8EW0CIC-h-H-jBVSgFjg3Eny2u33qF7BDnWFzo7fGfZ7_qc9P",
];
const publicIp = await this.getPublicIp();
this.configService.writeCodexConfigFile(publicIp, bootstrapNodes);
const command = `"${executable}" ${args.join(" ")}`;
console.log("command: " + command);
console.log("\n\n");
var child = spawn(executable, args, {
detached: true,
//stdio: ["ignore", "ignore", "ignore"],
});
child.stdout.on("data", (data) => {
console.log(`stdout: ${data}`);
});
child.stderr.on("data", (data) => {
console.error(`stderr: ${data}`);
});
child.on("close", (code) => {
console.log(`child process exited with code ${code}`);
});
child.unref();
await new Promise((resolve) => setTimeout(resolve, 2000));
return;
}; };
} }

View File

@ -35,6 +35,7 @@ import { Installer } from "./handlers/installer.js";
import { ShellService } from "./services/shellService.js"; import { ShellService } from "./services/shellService.js";
import { OsService } from "./services/osService.js"; import { OsService } from "./services/osService.js";
import { ProcessControl } from "./handlers/processControl.js"; import { ProcessControl } from "./handlers/processControl.js";
import { CodexGlobals } from "./services/codexGlobals.js";
async function showNavigationMenu() { async function showNavigationMenu() {
console.log("\n"); console.log("\n");
@ -133,14 +134,20 @@ export async function main() {
new DataDirMover(fsService, uiService), new DataDirMover(fsService, uiService),
); );
const codexGlobals = new CodexGlobals();
const processControl = new ProcessControl( const processControl = new ProcessControl(
configService, configService,
shellService, shellService,
osService, osService,
fsService, fsService,
); );
console.log("ip: " + (await codexGlobals.getPublicIp()));
console.log("spr: " + (await codexGlobals.getTestnetSprs()));
//await processControl.doThing(); //await processControl.doThing();
await processControl.detectThing(); // await processControl.detectThing();
return; return;
await mainMenu.show(); await mainMenu.show();

View File

@ -0,0 +1,12 @@
import axios from "axios";
export class CodexGlobals {
getPublicIp = async () => {
return (await axios.get(`https://ip.codex.storage`)).data;
};
getTestnetSPRs = async () => {
const result = (await axios.get(`https://spr.codex.storage/testnet`)).data;
return result.split("\n").filter((line) => line.length > 0);
};
}

View File

@ -77,7 +77,23 @@ export class ConfigService {
return this.fs.pathJoin([this.config.logsDir, "codex.log"]); return this.fs.pathJoin([this.config.logsDir, "codex.log"]);
}; };
missing = (name) => {
throw new Error(`Missing config value: ${name}`);
};
validateConfiguration = () => {
if (this.config.codexExe.length < 1) this.missing("codexExe");
if (this.config.codexConfigFilePath.length < 1)
this.missing("codexConfigFilePath");
if (this.config.dataDir.length < 1) this.missing("dataDir");
if (this.config.logsDir.length < 1) this.missing("logsDir");
if (this.config.storageQuota < 1024 * 1024 * 100)
throw new Error("Storage quota must be at least 100MB");
};
writeCodexConfigFile = (publicIp, bootstrapNodes) => { writeCodexConfigFile = (publicIp, bootstrapNodes) => {
this.validateConfiguration();
const nl = "\n"; const nl = "\n";
const bootNodes = bootstrapNodes.map((v) => `"${v}"`).join(","); const bootNodes = bootstrapNodes.map((v) => `"${v}"`).join(",");

View File

@ -1,4 +1,5 @@
import os from "os"; import os from "os";
import psList from "ps-list";
export class OsService { export class OsService {
constructor() { constructor() {
@ -20,4 +21,8 @@ export class OsService {
getWorkingDir = () => { getWorkingDir = () => {
return process.cwd(); return process.cwd();
}; };
listProcesses = async () => {
await psList();
};
} }

View File

@ -1,4 +1,4 @@
import { exec } from "child_process"; import { exec, spawn } from "child_process";
import { promisify } from "util"; import { promisify } from "util";
export class ShellService { export class ShellService {
@ -6,7 +6,7 @@ export class ShellService {
this.execAsync = promisify(exec); this.execAsync = promisify(exec);
} }
async run(command) { run = async (command) => {
try { try {
const { stdout, stderr } = await this.execAsync(command); const { stdout, stderr } = await this.execAsync(command);
return stdout; return stdout;
@ -14,5 +14,28 @@ export class ShellService {
console.error("Error:", error.message); console.error("Error:", error.message);
throw error; throw error;
} }
} };
spawnDetachedProcess = async (cmd, args) => {
var child = spawn(cmd, args, {
detached: true,
stdio: ["ignore", "ignore", "ignore"],
});
// child.stdout.on("data", (data) => {
// console.log(`stdout: ${data}`);
// });
// child.stderr.on("data", (data) => {
// console.error(`stderr: ${data}`);
// });
// child.on("close", (code) => {
// console.log(`child process exited with code ${code}`);
// });
child.unref();
await new Promise((resolve) => setTimeout(resolve, 2000));
};
} }