mirror of
https://github.com/logos-storage/logos-storage-installer.git
synced 2026-01-02 13:33:11 +00:00
sets up config service codex-config-file saving
This commit is contained in:
parent
4341a67ebe
commit
0f69d61e8e
@ -25,6 +25,9 @@ export const mockFsService = {
|
||||
readDir: vi.fn(),
|
||||
makeDir: vi.fn(),
|
||||
deleteDir: vi.fn(),
|
||||
readJsonFile: vi.fn(),
|
||||
writeJsonFile: vi.fn(),
|
||||
writeFile: vi.fn(),
|
||||
};
|
||||
|
||||
export const mockShellService = {
|
||||
|
||||
@ -3,6 +3,7 @@ import { spawn, exec } from "child_process";
|
||||
|
||||
export class ProcessControl {
|
||||
constructor(configService, shellService, osService, fsService) {
|
||||
this.configService = configService;
|
||||
this.config = configService.get();
|
||||
this.shell = shellService;
|
||||
this.os = osService;
|
||||
@ -18,28 +19,17 @@ export class ProcessControl {
|
||||
} else {
|
||||
return await this.shell.run("curl -s https://ip.codex.storage");
|
||||
}
|
||||
}
|
||||
|
||||
getLogFile = () =>{
|
||||
// function getCurrentLogFile(config) {
|
||||
// const timestamp = new Date()
|
||||
// .toISOString()
|
||||
// .replaceAll(":", "-")
|
||||
// .replaceAll(".", "-");
|
||||
// return path.join(config.logsDir, `codex_${timestamp}.log`);
|
||||
// }
|
||||
// todo, maybe use timestamp
|
||||
|
||||
return this.fs.pathJoin([this.config.logsDir, "codex.log"]);
|
||||
}
|
||||
};
|
||||
|
||||
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");
|
||||
if (this.config.dataDir.length < 1)
|
||||
throw new Error("Missing config: dataDir");
|
||||
if (this.config.logsDir.length < 1)
|
||||
throw new Error("Missing config: logsDir");
|
||||
|
||||
console.log("start a codex detached");
|
||||
|
||||
console.log("nat: " + await this.getPublicIp());
|
||||
console.log("nat: " + (await this.getPublicIp()));
|
||||
console.log("logs dir: " + this.getLogFile());
|
||||
console.log("data dir: " + this.config.dataDir);
|
||||
console.log("api port: " + this.config.ports.apiPort);
|
||||
@ -64,10 +54,15 @@ export class ProcessControl {
|
||||
console.log("command: " + command);
|
||||
console.log("\n\n");
|
||||
|
||||
var child = spawn(executable, args, { detached: true, stdio: ['ignore', 'ignore', 'ignore']});
|
||||
this.configService.writeCodexConfigFile();
|
||||
|
||||
var child = spawn(executable, args, {
|
||||
detached: true,
|
||||
stdio: ["ignore", "ignore", "ignore"],
|
||||
});
|
||||
child.unref();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
11
src/main.js
11
src/main.js
@ -99,9 +99,9 @@ export async function main() {
|
||||
process.on("SIGTERM", handleExit);
|
||||
process.on("SIGQUIT", handleExit);
|
||||
|
||||
const configService = new ConfigService();
|
||||
const uiService = new UiService();
|
||||
const fsService = new FsService();
|
||||
const configService = new ConfigService(fsService);
|
||||
const pathSelector = new PathSelector(uiService, new MenuLoop(), fsService);
|
||||
const numberSelector = new NumberSelector(uiService);
|
||||
const shellService = new ShellService();
|
||||
@ -133,15 +133,18 @@ export async function main() {
|
||||
new DataDirMover(fsService, uiService),
|
||||
);
|
||||
|
||||
const processControl = new ProcessControl(configService, shellService, osService, fsService);
|
||||
const processControl = new ProcessControl(
|
||||
configService,
|
||||
shellService,
|
||||
osService,
|
||||
fsService,
|
||||
);
|
||||
await processControl.doThing();
|
||||
return;
|
||||
|
||||
await mainMenu.show();
|
||||
return;
|
||||
|
||||
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
console.log("\n" + chalk.cyanBright(ASCII_ART));
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { getAppDataDir } from "../utils/appData.js";
|
||||
import {
|
||||
getAppDataDir,
|
||||
getCodexBinPath,
|
||||
getCodexConfigFilePath,
|
||||
getCodexDataDirDefaultPath,
|
||||
getCodexLogsDefaultPath,
|
||||
} from "../utils/appData.js";
|
||||
@ -11,6 +10,7 @@ const defaultConfig = {
|
||||
codexExe: "",
|
||||
// User-selected config options:
|
||||
codexInstallPath: getCodexBinPath(),
|
||||
codexConfigFilePath: getCodexConfigFilePath(),
|
||||
dataDir: getCodexDataDirDefaultPath(),
|
||||
logsDir: getCodexLogsDefaultPath(),
|
||||
storageQuota: 8 * 1024 * 1024 * 1024,
|
||||
@ -22,7 +22,8 @@ const defaultConfig = {
|
||||
};
|
||||
|
||||
export class ConfigService {
|
||||
constructor() {
|
||||
constructor(fsService) {
|
||||
this.fs = fsService;
|
||||
this.loadConfig();
|
||||
}
|
||||
|
||||
@ -33,11 +34,12 @@ export class ConfigService {
|
||||
loadConfig = () => {
|
||||
const filePath = this.getConfigFilename();
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
if (!this.fs.isFile(filePath)) {
|
||||
this.config = defaultConfig;
|
||||
this.saveConfig();
|
||||
} else {
|
||||
this.config = this.fs.readJsonFile(filePath);
|
||||
}
|
||||
this.config = JSON.parse(fs.readFileSync(filePath));
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to load config file from '${filePath}' error: '${error}'.`,
|
||||
@ -49,7 +51,7 @@ export class ConfigService {
|
||||
saveConfig = () => {
|
||||
const filePath = this.getConfigFilename();
|
||||
try {
|
||||
fs.writeFileSync(filePath, JSON.stringify(this.config));
|
||||
this.fs.writeJsonFile(filePath, this.config);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to save config file to '${filePath}' error: '${error}'.`,
|
||||
@ -59,6 +61,42 @@ export class ConfigService {
|
||||
};
|
||||
|
||||
getConfigFilename = () => {
|
||||
return path.join(getAppDataDir(), "config.json");
|
||||
return this.fs.pathJoin([getAppDataDir(), "config.json"]);
|
||||
};
|
||||
|
||||
getLogFilePath = () => {
|
||||
// function getCurrentLogFile(config) {
|
||||
// const timestamp = new Date()
|
||||
// .toISOString()
|
||||
// .replaceAll(":", "-")
|
||||
// .replaceAll(".", "-");
|
||||
// return path.join(config.logsDir, `codex_${timestamp}.log`);
|
||||
// }
|
||||
// todo, maybe use timestamp
|
||||
|
||||
return this.fs.pathJoin([this.config.logsDir, "codex.log"]);
|
||||
};
|
||||
|
||||
writeCodexConfigFile = (publicIp, bootstrapNodes) => {
|
||||
const nl = "\n";
|
||||
const bootNodes = bootstrapNodes.join(",");
|
||||
|
||||
this.fs.writeFile(
|
||||
this.config.codexConfigFilePath,
|
||||
`data-dir="${this.format(this.config.dataDir)}"${nl}` +
|
||||
`log-level=DEBUG${nl}` +
|
||||
`log-file="${this.format(this.getLogFilePath())}"${nl}` +
|
||||
`storage-quota=${this.config.storageQuota}${nl}` +
|
||||
`disc-port=${this.config.ports.discPort}${nl}` +
|
||||
`listen-addrs=/ip4/0.0.0.0/tcp/${this.config.ports.listenPort}${nl}` +
|
||||
`api-port=${this.config.ports.apiPort}${nl}` +
|
||||
`nat=extip:${publicIp}${nl}` +
|
||||
`api-cors-origin="*"${nl}` +
|
||||
`bootstrap-node=[${bootNodes}]`,
|
||||
);
|
||||
};
|
||||
|
||||
format = (str) => {
|
||||
return str.replaceAll("\\", "/");
|
||||
};
|
||||
}
|
||||
|
||||
129
src/services/configService.test.js
Normal file
129
src/services/configService.test.js
Normal file
@ -0,0 +1,129 @@
|
||||
import { describe, beforeEach, it, expect, vi } from "vitest";
|
||||
import { ConfigService } from "./configService.js";
|
||||
import { mockFsService } from "../__mocks__/service.mocks.js";
|
||||
import {
|
||||
getAppDataDir,
|
||||
getCodexBinPath,
|
||||
getCodexConfigFilePath,
|
||||
getCodexDataDirDefaultPath,
|
||||
getCodexLogsDefaultPath,
|
||||
} from "../utils/appData.js";
|
||||
|
||||
describe("ConfigService", () => {
|
||||
const configPath = "/path/to/config.json";
|
||||
const expectedDefaultConfig = {
|
||||
codexExe: "",
|
||||
codexInstallPath: getCodexBinPath(),
|
||||
codexConfigFilePath: getCodexConfigFilePath(),
|
||||
dataDir: getCodexDataDirDefaultPath(),
|
||||
logsDir: getCodexLogsDefaultPath(),
|
||||
storageQuota: 8 * 1024 * 1024 * 1024,
|
||||
ports: {
|
||||
discPort: 8090,
|
||||
listenPort: 8070,
|
||||
apiPort: 8080,
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
|
||||
mockFsService.pathJoin.mockReturnValue(configPath);
|
||||
});
|
||||
|
||||
describe("constructor", () => {
|
||||
it("formats the config file path", () => {
|
||||
new ConfigService(mockFsService);
|
||||
|
||||
expect(mockFsService.pathJoin).toHaveBeenCalledWith([
|
||||
getAppDataDir(),
|
||||
"config.json",
|
||||
]);
|
||||
});
|
||||
|
||||
it("saves the default config when the config.json file does not exist", () => {
|
||||
mockFsService.isFile.mockReturnValue(false);
|
||||
|
||||
const service = new ConfigService(mockFsService);
|
||||
|
||||
expect(mockFsService.isFile).toHaveBeenCalledWith(configPath);
|
||||
expect(mockFsService.readJsonFile).not.toHaveBeenCalled();
|
||||
expect(mockFsService.writeJsonFile).toHaveBeenCalledWith(
|
||||
configPath,
|
||||
service.config,
|
||||
);
|
||||
expect(service.config).toEqual(expectedDefaultConfig);
|
||||
});
|
||||
|
||||
it("loads the config.json file when it does exist", () => {
|
||||
mockFsService.isFile.mockReturnValue(true);
|
||||
const savedConfig = {
|
||||
isTestConfig: "Yes, very",
|
||||
};
|
||||
mockFsService.readJsonFile.mockReturnValue(savedConfig);
|
||||
|
||||
const service = new ConfigService(mockFsService);
|
||||
|
||||
expect(mockFsService.isFile).toHaveBeenCalledWith(configPath);
|
||||
expect(mockFsService.readJsonFile).toHaveBeenCalledWith(configPath);
|
||||
expect(mockFsService.writeJsonFile).not.toHaveBeenCalled();
|
||||
expect(service.config).toEqual(savedConfig);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getLogFilePath", () => {
|
||||
it("joins the logsDir with the log filename", () => {
|
||||
const service = new ConfigService(mockFsService);
|
||||
|
||||
const result = "path/to/codex.log";
|
||||
mockFsService.pathJoin.mockReturnValue(result);
|
||||
|
||||
expect(service.getLogFilePath()).toBe(result);
|
||||
expect(mockFsService.pathJoin).toHaveBeenCalledWith([
|
||||
expectedDefaultConfig.logsDir,
|
||||
"codex.log",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("writecodexConfigFile", () => {
|
||||
const logsPath = "C:\\path\\codex.log";
|
||||
var configService;
|
||||
|
||||
beforeEach(() => {
|
||||
// use the default config:
|
||||
mockFsService.isFile.mockReturnValue(false);
|
||||
|
||||
configService = new ConfigService(mockFsService);
|
||||
configService.getLogFilePath = vi.fn();
|
||||
configService.getLogFilePath.mockReturnValue(logsPath);
|
||||
});
|
||||
|
||||
function formatPath(str) {
|
||||
return str.replaceAll("\\", "/");
|
||||
}
|
||||
|
||||
it("writes the config file values to the config TOML file", () => {
|
||||
const publicIp = "1.2.3.4";
|
||||
const bootstrapNodes = ["boot111", "boot222", "boot333"];
|
||||
|
||||
configService.writeCodexConfigFile(publicIp, bootstrapNodes);
|
||||
|
||||
const newLine = "\n";
|
||||
|
||||
expect(mockFsService.writeFile).toHaveBeenCalledWith(
|
||||
expectedDefaultConfig.codexConfigFilePath,
|
||||
`data-dir=\"${formatPath(expectedDefaultConfig.dataDir)}"${newLine}` +
|
||||
`log-level=DEBUG${newLine}` +
|
||||
`log-file="${formatPath(logsPath)}"${newLine}` +
|
||||
`storage-quota=${expectedDefaultConfig.storageQuota}${newLine}` +
|
||||
`disc-port=${expectedDefaultConfig.ports.discPort}${newLine}` +
|
||||
`listen-addrs=/ip4/0.0.0.0/tcp/${expectedDefaultConfig.ports.listenPort}${newLine}` +
|
||||
`api-port=${expectedDefaultConfig.ports.apiPort}${newLine}` +
|
||||
`nat=extip:${publicIp}${newLine}` +
|
||||
`api-cors-origin="*"${newLine}` +
|
||||
`bootstrap-node=[${bootstrapNodes.join(",")}]`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -54,4 +54,17 @@ export class FsService {
|
||||
deleteDir = (dir) => {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
};
|
||||
|
||||
readJsonFile = (filePath) => {
|
||||
return JSON.parse(fs.readFileSync(filePath));
|
||||
};
|
||||
|
||||
writeJsonFile = (filePath, jsonObject) => {
|
||||
fs.writeFileSync(filePath, JSON.stringify(jsonObject));
|
||||
};
|
||||
|
||||
writeFile = (filePath, content) => {
|
||||
console.log("filepath: " + filePath);
|
||||
fs.writeFileSync(filePath, content);
|
||||
};
|
||||
}
|
||||
|
||||
@ -13,6 +13,10 @@ export function getCodexBinPath() {
|
||||
return ensureExists(path.join(appData("codex"), "bin"));
|
||||
}
|
||||
|
||||
export function getCodexConfigFilePath() {
|
||||
return path.join(appData("codex"), "bin", "config.toml");
|
||||
}
|
||||
|
||||
export function getCodexDataDirDefaultPath() {
|
||||
// This path does not exist on first startup. That's good: Codex will
|
||||
// create it with the required access permissions.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user