mirror of
https://github.com/logos-storage/logos-storage-installer.git
synced 2026-01-04 06:23:07 +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(),
|
readDir: vi.fn(),
|
||||||
makeDir: vi.fn(),
|
makeDir: vi.fn(),
|
||||||
deleteDir: vi.fn(),
|
deleteDir: vi.fn(),
|
||||||
|
readJsonFile: vi.fn(),
|
||||||
|
writeJsonFile: vi.fn(),
|
||||||
|
writeFile: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mockShellService = {
|
export const mockShellService = {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { spawn, exec } from "child_process";
|
|||||||
|
|
||||||
export class ProcessControl {
|
export class ProcessControl {
|
||||||
constructor(configService, shellService, osService, fsService) {
|
constructor(configService, shellService, osService, fsService) {
|
||||||
|
this.configService = configService;
|
||||||
this.config = configService.get();
|
this.config = configService.get();
|
||||||
this.shell = shellService;
|
this.shell = shellService;
|
||||||
this.os = osService;
|
this.os = osService;
|
||||||
@ -18,28 +19,17 @@ export class ProcessControl {
|
|||||||
} else {
|
} else {
|
||||||
return await this.shell.run("curl -s https://ip.codex.storage");
|
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 () => {
|
doThing = async () => {
|
||||||
if (this.config.dataDir.length < 1) throw new Error("Missing config: dataDir");
|
if (this.config.dataDir.length < 1)
|
||||||
if (this.config.logsDir.length < 1) throw new Error("Missing config: logsDir");
|
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("start a codex detached");
|
||||||
|
|
||||||
console.log("nat: " + await this.getPublicIp());
|
console.log("nat: " + (await this.getPublicIp()));
|
||||||
console.log("logs dir: " + this.getLogFile());
|
console.log("logs dir: " + this.getLogFile());
|
||||||
console.log("data dir: " + this.config.dataDir);
|
console.log("data dir: " + this.config.dataDir);
|
||||||
console.log("api port: " + this.config.ports.apiPort);
|
console.log("api port: " + this.config.ports.apiPort);
|
||||||
@ -64,10 +54,15 @@ export class ProcessControl {
|
|||||||
console.log("command: " + command);
|
console.log("command: " + command);
|
||||||
console.log("\n\n");
|
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();
|
child.unref();
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/main.js
11
src/main.js
@ -99,9 +99,9 @@ export async function main() {
|
|||||||
process.on("SIGTERM", handleExit);
|
process.on("SIGTERM", handleExit);
|
||||||
process.on("SIGQUIT", handleExit);
|
process.on("SIGQUIT", handleExit);
|
||||||
|
|
||||||
const configService = new ConfigService();
|
|
||||||
const uiService = new UiService();
|
const uiService = new UiService();
|
||||||
const fsService = new FsService();
|
const fsService = new FsService();
|
||||||
|
const configService = new ConfigService(fsService);
|
||||||
const pathSelector = new PathSelector(uiService, new MenuLoop(), fsService);
|
const pathSelector = new PathSelector(uiService, new MenuLoop(), fsService);
|
||||||
const numberSelector = new NumberSelector(uiService);
|
const numberSelector = new NumberSelector(uiService);
|
||||||
const shellService = new ShellService();
|
const shellService = new ShellService();
|
||||||
@ -133,15 +133,18 @@ export async function main() {
|
|||||||
new DataDirMover(fsService, uiService),
|
new DataDirMover(fsService, uiService),
|
||||||
);
|
);
|
||||||
|
|
||||||
const processControl = new ProcessControl(configService, shellService, osService, fsService);
|
const processControl = new ProcessControl(
|
||||||
|
configService,
|
||||||
|
shellService,
|
||||||
|
osService,
|
||||||
|
fsService,
|
||||||
|
);
|
||||||
await processControl.doThing();
|
await processControl.doThing();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await mainMenu.show();
|
await mainMenu.show();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
console.log("\n" + chalk.cyanBright(ASCII_ART));
|
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 {
|
import {
|
||||||
|
getAppDataDir,
|
||||||
getCodexBinPath,
|
getCodexBinPath,
|
||||||
|
getCodexConfigFilePath,
|
||||||
getCodexDataDirDefaultPath,
|
getCodexDataDirDefaultPath,
|
||||||
getCodexLogsDefaultPath,
|
getCodexLogsDefaultPath,
|
||||||
} from "../utils/appData.js";
|
} from "../utils/appData.js";
|
||||||
@ -11,6 +10,7 @@ const defaultConfig = {
|
|||||||
codexExe: "",
|
codexExe: "",
|
||||||
// User-selected config options:
|
// User-selected config options:
|
||||||
codexInstallPath: getCodexBinPath(),
|
codexInstallPath: getCodexBinPath(),
|
||||||
|
codexConfigFilePath: getCodexConfigFilePath(),
|
||||||
dataDir: getCodexDataDirDefaultPath(),
|
dataDir: getCodexDataDirDefaultPath(),
|
||||||
logsDir: getCodexLogsDefaultPath(),
|
logsDir: getCodexLogsDefaultPath(),
|
||||||
storageQuota: 8 * 1024 * 1024 * 1024,
|
storageQuota: 8 * 1024 * 1024 * 1024,
|
||||||
@ -22,7 +22,8 @@ const defaultConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class ConfigService {
|
export class ConfigService {
|
||||||
constructor() {
|
constructor(fsService) {
|
||||||
|
this.fs = fsService;
|
||||||
this.loadConfig();
|
this.loadConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,11 +34,12 @@ export class ConfigService {
|
|||||||
loadConfig = () => {
|
loadConfig = () => {
|
||||||
const filePath = this.getConfigFilename();
|
const filePath = this.getConfigFilename();
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!this.fs.isFile(filePath)) {
|
||||||
this.config = defaultConfig;
|
this.config = defaultConfig;
|
||||||
this.saveConfig();
|
this.saveConfig();
|
||||||
|
} else {
|
||||||
|
this.config = this.fs.readJsonFile(filePath);
|
||||||
}
|
}
|
||||||
this.config = JSON.parse(fs.readFileSync(filePath));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
`Failed to load config file from '${filePath}' error: '${error}'.`,
|
`Failed to load config file from '${filePath}' error: '${error}'.`,
|
||||||
@ -49,7 +51,7 @@ export class ConfigService {
|
|||||||
saveConfig = () => {
|
saveConfig = () => {
|
||||||
const filePath = this.getConfigFilename();
|
const filePath = this.getConfigFilename();
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(filePath, JSON.stringify(this.config));
|
this.fs.writeJsonFile(filePath, this.config);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
`Failed to save config file to '${filePath}' error: '${error}'.`,
|
`Failed to save config file to '${filePath}' error: '${error}'.`,
|
||||||
@ -59,6 +61,42 @@ export class ConfigService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
getConfigFilename = () => {
|
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) => {
|
deleteDir = (dir) => {
|
||||||
fs.rmSync(dir, { recursive: true, force: true });
|
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"));
|
return ensureExists(path.join(appData("codex"), "bin"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCodexConfigFilePath() {
|
||||||
|
return path.join(appData("codex"), "bin", "config.toml");
|
||||||
|
}
|
||||||
|
|
||||||
export function getCodexDataDirDefaultPath() {
|
export function getCodexDataDirDefaultPath() {
|
||||||
// This path does not exist on first startup. That's good: Codex will
|
// This path does not exist on first startup. That's good: Codex will
|
||||||
// create it with the required access permissions.
|
// create it with the required access permissions.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user