diff --git a/src/__mocks__/service.mocks.js b/src/__mocks__/service.mocks.js index 5de6f87..ec36a5f 100644 --- a/src/__mocks__/service.mocks.js +++ b/src/__mocks__/service.mocks.js @@ -15,6 +15,7 @@ export const mockConfigService = { get: vi.fn(), saveConfig: vi.fn(), loadConfig: vi.fn(), + writeCodexConfigFile: vi.fn(), }; export const mockFsService = { @@ -32,6 +33,7 @@ export const mockFsService = { export const mockShellService = { run: vi.fn(), + spawnDetachedProcess: vi.fn(), }; export const mockOsService = { @@ -39,4 +41,10 @@ export const mockOsService = { isDarwin: vi.fn(), isLinux: vi.fn(), getWorkingDir: vi.fn(), + listProcesses: vi.fn(), +}; + +export const mockCodexGlobals = { + getPublicIp: vi.fn(), + getTestnetSPRs: vi.fn(), }; diff --git a/src/handlers/processControl.js b/src/handlers/processControl.js index 30c0119..96a7d1c 100644 --- a/src/handlers/processControl.js +++ b/src/handlers/processControl.js @@ -1,95 +1,49 @@ -import fs from "fs"; -import { spawn, exec } from "child_process"; -import psList from 'ps-list'; - export class ProcessControl { - constructor(configService, shellService, osService, fsService) { + constructor(configService, shellService, osService, fsService, codexGlobals) { this.configService = configService; this.config = configService.get(); this.shell = shellService; this.os = osService; this.fs = fsService; + this.codexGlobals; } - getPublicIp = async () => { + getCodexProcesses = async () => { + const processes = await this.os.listProcesses(); if (this.os.isWindows()) { - const result = await this.shell.run( - "for /f \"delims=\" %a in ('curl -s --ssl-reqd ip.codex.storage') do @echo %a", - ); - return result.trim(); + return processes.filter((p) => p.name === "codex.exe"); } else { - return await this.shell.run("curl -s https://ip.codex.storage"); + return processes.filter((p) => p.name === "codex"); } }; - detectThing = async () => { - console.log("detecting..."); + getNumberOfCodexProcesses = async () => { + return (await this.getCodexProcesses()).length; + }; - const processes = await psList(); - const codexProcesses = processes.filter((p) => p.name === "codex.exe"); - if (codexProcesses.length > 0) { - 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..."); + stopCodexProcess = async () => { + const processes = await this.getCodexProcesses(); + if (processes.length < 1) throw new Error("No codex process found"); + const pid = processes[0].pid; process.kill(pid, "SIGINT"); - await new Promise((resolve) => setTimeout(resolve, 2000)); - } + }; - doThing = async () => { - if (this.config.dataDir.length < 1) - throw new Error("Missing config: dataDir"); - if (this.config.logsDir.length < 1) - throw new Error("Missing config: logsDir"); + startCodexProcess = async () => { + this.saveCodexConfigFile(); + this.startCodex(); + }; - 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 args = [`--config-file=${this.config.codexConfigFilePath}`]; - const bootstrapNodes = [ - "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; + await this.shell.spawnDetachedProcess(executable, args); }; } diff --git a/src/main.js b/src/main.js index b7ad534..ad7f22a 100644 --- a/src/main.js +++ b/src/main.js @@ -35,6 +35,7 @@ import { Installer } from "./handlers/installer.js"; import { ShellService } from "./services/shellService.js"; import { OsService } from "./services/osService.js"; import { ProcessControl } from "./handlers/processControl.js"; +import { CodexGlobals } from "./services/codexGlobals.js"; async function showNavigationMenu() { console.log("\n"); @@ -133,14 +134,20 @@ export async function main() { new DataDirMover(fsService, uiService), ); + const codexGlobals = new CodexGlobals(); + const processControl = new ProcessControl( configService, shellService, osService, fsService, ); + + console.log("ip: " + (await codexGlobals.getPublicIp())); + console.log("spr: " + (await codexGlobals.getTestnetSprs())); + //await processControl.doThing(); - await processControl.detectThing(); + // await processControl.detectThing(); return; await mainMenu.show(); diff --git a/src/services/codexGlobals.js b/src/services/codexGlobals.js new file mode 100644 index 0000000..d48fa6c --- /dev/null +++ b/src/services/codexGlobals.js @@ -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); + }; +} diff --git a/src/services/configService.js b/src/services/configService.js index 1df6c13..91169a5 100644 --- a/src/services/configService.js +++ b/src/services/configService.js @@ -77,7 +77,23 @@ export class ConfigService { 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) => { + this.validateConfiguration(); + const nl = "\n"; const bootNodes = bootstrapNodes.map((v) => `"${v}"`).join(","); diff --git a/src/services/osService.js b/src/services/osService.js index 3ec8f05..efc12ef 100644 --- a/src/services/osService.js +++ b/src/services/osService.js @@ -1,4 +1,5 @@ import os from "os"; +import psList from "ps-list"; export class OsService { constructor() { @@ -20,4 +21,8 @@ export class OsService { getWorkingDir = () => { return process.cwd(); }; + + listProcesses = async () => { + await psList(); + }; } diff --git a/src/services/shellService.js b/src/services/shellService.js index df9f63e..79ae419 100644 --- a/src/services/shellService.js +++ b/src/services/shellService.js @@ -1,4 +1,4 @@ -import { exec } from "child_process"; +import { exec, spawn } from "child_process"; import { promisify } from "util"; export class ShellService { @@ -6,7 +6,7 @@ export class ShellService { this.execAsync = promisify(exec); } - async run(command) { + run = async (command) => { try { const { stdout, stderr } = await this.execAsync(command); return stdout; @@ -14,5 +14,28 @@ export class ShellService { console.error("Error:", error.message); 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)); + }; }