mirror of
https://github.com/logos-storage/logos-storage-installer.git
synced 2026-01-08 00:13:07 +00:00
sets up config menu and config service
This commit is contained in:
parent
8d0418be81
commit
a1d6056974
@ -1,162 +0,0 @@
|
||||
import inquirer from "inquirer";
|
||||
import chalk from "chalk";
|
||||
import { showErrorMessage, showInfoMessage } from "./utils/messages.js";
|
||||
import { isDir, showPathSelector } from "./utils/pathSelector.js";
|
||||
import { saveConfig } from "./services/config.js";
|
||||
import { showNumberSelector } from "./utils/numberSelector.js";
|
||||
import fs from "fs-extra";
|
||||
|
||||
function bytesAmountToString(numBytes) {
|
||||
const units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
|
||||
var value = numBytes;
|
||||
var index = 0;
|
||||
while (value > 1024) {
|
||||
index = index + 1;
|
||||
value = value / 1024;
|
||||
}
|
||||
|
||||
if (index == 0) return `${numBytes} Bytes`;
|
||||
return `${numBytes} Bytes (${value} ${units[index]})`;
|
||||
}
|
||||
|
||||
async function showStorageQuotaSelector(config) {
|
||||
console.log(showInfoMessage('You can use: "GB" or "gb", etc.'));
|
||||
const result = await showNumberSelector(
|
||||
config.storageQuota,
|
||||
"Storage quota",
|
||||
true,
|
||||
);
|
||||
if (result < 100 * 1024 * 1024) {
|
||||
console.log(showErrorMessage("Storage quote should be >= 100mb."));
|
||||
return config.storageQuota;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function showConfigMenu(config) {
|
||||
var newDataDir = config.dataDir;
|
||||
try {
|
||||
while (true) {
|
||||
console.log(showInfoMessage("Codex Configuration"));
|
||||
const { choice } = await inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: "list",
|
||||
name: "choice",
|
||||
message: "Select to edit:",
|
||||
choices: [
|
||||
`1. Data path = "${newDataDir}"`,
|
||||
`2. Logs path = "${config.logsDir}"`,
|
||||
`3. Storage quota = ${bytesAmountToString(config.storageQuota)}`,
|
||||
`4. Discovery port = ${config.ports.discPort}`,
|
||||
`5. P2P listen port = ${config.ports.listenPort}`,
|
||||
`6. API port = ${config.ports.apiPort}`,
|
||||
"7. Save changes and exit",
|
||||
"8. Discard changes and exit",
|
||||
],
|
||||
pageSize: 8,
|
||||
loop: true,
|
||||
},
|
||||
])
|
||||
.catch(() => {
|
||||
return;
|
||||
});
|
||||
|
||||
switch (choice.split(".")[0]) {
|
||||
case "1":
|
||||
newDataDir = await showPathSelector(config.dataDir, false);
|
||||
if (isDir(newDataDir)) {
|
||||
console.log(
|
||||
showInfoMessage(
|
||||
"Warning: The new data path already exists. Make sure you know what you're doing.",
|
||||
),
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "2":
|
||||
config.logsDir = await showPathSelector(config.logsDir, true);
|
||||
break;
|
||||
case "3":
|
||||
config.storageQuota = await showStorageQuotaSelector(config);
|
||||
break;
|
||||
case "4":
|
||||
config.ports.discPort = await showNumberSelector(
|
||||
config.ports.discPort,
|
||||
"Discovery Port (UDP)",
|
||||
false,
|
||||
);
|
||||
break;
|
||||
case "5":
|
||||
config.ports.listenPort = await showNumberSelector(
|
||||
config.ports.listenPort,
|
||||
"Listen Port (TCP)",
|
||||
false,
|
||||
);
|
||||
break;
|
||||
case "6":
|
||||
config.ports.apiPort = await showNumberSelector(
|
||||
config.ports.apiPort,
|
||||
"API Port (TCP)",
|
||||
false,
|
||||
);
|
||||
break;
|
||||
case "7":
|
||||
// save changes, back to main menu
|
||||
config = updateDataDir(config, newDataDir);
|
||||
saveConfig(config);
|
||||
return;
|
||||
case "8":
|
||||
// discard changes, back to main menu
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(chalk.red("An error occurred:", error.message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function updateDataDir(config, newDataDir) {
|
||||
if (config.dataDir == newDataDir) return config;
|
||||
|
||||
// The Codex dataDir is a little strange:
|
||||
// If the old one is empty: The new one should not exist, so that codex creates it
|
||||
// with the correct security permissions.
|
||||
// If the old one does exist: We move it.
|
||||
|
||||
if (isDir(config.dataDir)) {
|
||||
console.log(
|
||||
showInfoMessage(
|
||||
"Moving Codex data folder...\n" +
|
||||
`From: "${config.dataDir}"\n` +
|
||||
`To: "${newDataDir}"`,
|
||||
),
|
||||
);
|
||||
|
||||
try {
|
||||
fs.moveSync(config.dataDir, newDataDir);
|
||||
} catch (error) {
|
||||
console.log(
|
||||
showErrorMessage("Error while moving dataDir: " + error.message),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
// Old data dir does not exist.
|
||||
if (isDir(newDataDir)) {
|
||||
console.log(
|
||||
showInfoMessage(
|
||||
"Warning: the selected data path already exists.\n" +
|
||||
`New data path = "${newDataDir}"\n` +
|
||||
"Codex may overwrite data in this folder.\n" +
|
||||
"Codex will fail to start if this folder does not have the required\n" +
|
||||
"security permissions.",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
config.dataDir = newDataDir;
|
||||
return config;
|
||||
}
|
||||
@ -12,7 +12,6 @@ import {
|
||||
showSuccessMessage,
|
||||
} from "../utils/messages.js";
|
||||
import { checkDependencies } from "../services/nodeService.js";
|
||||
import { saveConfig } from "../services/config.js";
|
||||
import { getCodexRootPath, getCodexBinPath } from "../utils/appdata.js";
|
||||
|
||||
const platform = os.platform();
|
||||
|
||||
@ -19,8 +19,7 @@ import {
|
||||
} from "./handlers/installationHandlers.js";
|
||||
import { runCodex, checkNodeStatus } from "./handlers/nodeHandlers.js";
|
||||
import { showInfoMessage } from "./utils/messages.js";
|
||||
import { loadConfig } from "./services/config.js";
|
||||
import { showConfigMenu } from "./configmenu.js";
|
||||
import { ConfigService } from "./services/configService.js";
|
||||
import { openCodexApp } from "./services/codexapp.js";
|
||||
|
||||
import { MainMenu } from "./ui/mainmenu.js";
|
||||
@ -90,9 +89,9 @@ export async function main() {
|
||||
process.on("SIGTERM", handleExit);
|
||||
process.on("SIGQUIT", handleExit);
|
||||
|
||||
const config = loadConfig();
|
||||
const configService = new ConfigService();
|
||||
const uiService = new UiService();
|
||||
const installMenu = new InstallMenu(uiService, config);
|
||||
const installMenu = new InstallMenu(uiService, configService);
|
||||
const mainMenu = new MainMenu(uiService, installMenu);
|
||||
|
||||
await mainMenu.show();
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { getAppDataDir } from "../utils/appdata.js";
|
||||
import {
|
||||
getCodexBinPath,
|
||||
getCodexDataDirDefaultPath,
|
||||
getCodexLogsDefaultPath,
|
||||
} from "../utils/appdata.js";
|
||||
|
||||
const defaultConfig = {
|
||||
codexExe: "",
|
||||
// User-selected config options:
|
||||
codexPath: getCodexBinPath(),
|
||||
dataDir: getCodexDataDirDefaultPath(),
|
||||
logsDir: getCodexLogsDefaultPath(),
|
||||
storageQuota: 8 * 1024 * 1024 * 1024,
|
||||
ports: {
|
||||
discPort: 8090,
|
||||
listenPort: 8070,
|
||||
apiPort: 8080,
|
||||
},
|
||||
};
|
||||
|
||||
function getConfigFilename() {
|
||||
return path.join(getAppDataDir(), "config.json");
|
||||
}
|
||||
|
||||
export function saveConfig(config) {
|
||||
const filePath = getConfigFilename();
|
||||
try {
|
||||
fs.writeFileSync(filePath, JSON.stringify(config));
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to save config file to '${filePath}' error: '${error}'.`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function loadConfig() {
|
||||
const filePath = getConfigFilename();
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
saveConfig(defaultConfig);
|
||||
return defaultConfig;
|
||||
}
|
||||
return JSON.parse(fs.readFileSync(filePath));
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to load config file from '${filePath}' error: '${error}'.`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
64
src/services/configService.js
Normal file
64
src/services/configService.js
Normal file
@ -0,0 +1,64 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { getAppDataDir } from "../utils/appdata.js";
|
||||
import {
|
||||
getCodexBinPath,
|
||||
getCodexDataDirDefaultPath,
|
||||
getCodexLogsDefaultPath,
|
||||
} from "../utils/appdata.js";
|
||||
|
||||
const defaultConfig = {
|
||||
codexExe: "",
|
||||
// User-selected config options:
|
||||
codexPath: getCodexBinPath(),
|
||||
dataDir: getCodexDataDirDefaultPath(),
|
||||
logsDir: getCodexLogsDefaultPath(),
|
||||
storageQuota: 8 * 1024 * 1024 * 1024,
|
||||
ports: {
|
||||
discPort: 8090,
|
||||
listenPort: 8070,
|
||||
apiPort: 8080,
|
||||
},
|
||||
};
|
||||
|
||||
export class ConfigService {
|
||||
constructor() {
|
||||
this.loadConfig();
|
||||
}
|
||||
|
||||
get = () => {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
loadConfig = () => {
|
||||
const filePath = this.getConfigFilename();
|
||||
try {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
this.config = defaultConfig;
|
||||
this.saveConfig();
|
||||
}
|
||||
this.config = JSON.parse(fs.readFileSync(filePath));
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to load config file from '${filePath}' error: '${error}'.`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
saveConfig = () => {
|
||||
const filePath = this.getConfigFilename();
|
||||
try {
|
||||
fs.writeFileSync(filePath, JSON.stringify(this.config));
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to save config file to '${filePath}' error: '${error}'.`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
getConfigFilename = () => {
|
||||
return path.join(getAppDataDir(), "config.json");
|
||||
}
|
||||
}
|
||||
167
src/ui/configmenu.js
Normal file
167
src/ui/configmenu.js
Normal file
@ -0,0 +1,167 @@
|
||||
export class ConfigMenu {
|
||||
constructor(uiService, configService, pathSelector, numberSelector) {
|
||||
this.ui = uiService;
|
||||
this.configService = configService;
|
||||
this.pathSelector = pathSelector;
|
||||
this.numberSelector = numberSelector;
|
||||
}
|
||||
|
||||
show = async() => {
|
||||
this.running = true;
|
||||
this.config = this.configService.get();
|
||||
while (this.running) {
|
||||
this.ui.showInfoMessage("Codex Configuration");
|
||||
await this.ui.askMultipleChoice("Select to edit:",[
|
||||
{
|
||||
label: `Data path = "${this.config.dataDir}"`,
|
||||
action: this.editDataDir,
|
||||
},
|
||||
{
|
||||
label: `Logs path = "${this.config.logsDir}"`,
|
||||
action: this.editLogsDir,
|
||||
},
|
||||
{
|
||||
label: `Storage quota = ${bytesAmountToString(this.config.storageQuota)}`,
|
||||
action: this.editStorageQuota,
|
||||
},
|
||||
{
|
||||
label: `Discovery port = ${this.config.ports.discPort}`,
|
||||
action: this.editDiscPort,
|
||||
},
|
||||
{
|
||||
label: `P2P listen port = ${this.config.ports.listenPort}`,
|
||||
action: this.editListenPort,
|
||||
},
|
||||
{
|
||||
label: `API port = ${this.config.ports.apiPort}`,
|
||||
action: this.editApiPort,
|
||||
},
|
||||
{
|
||||
label: "Save changes and exit",
|
||||
action: this.saveChangesAndExit,
|
||||
},
|
||||
{
|
||||
label: "Discard changes and exit",
|
||||
action: this.discardChangesAndExit,
|
||||
}
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
bytesAmountToString = (numBytes) => {
|
||||
const units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
|
||||
var value = numBytes;
|
||||
var index = 0;
|
||||
while (value > 1024) {
|
||||
index = index + 1;
|
||||
value = value / 1024;
|
||||
}
|
||||
|
||||
if (index == 0) return `${numBytes} Bytes`;
|
||||
return `${numBytes} Bytes (${value} ${units[index]})`;
|
||||
}
|
||||
|
||||
editDataDir = async () => {
|
||||
// todo
|
||||
// function updateDataDir(config, newDataDir) {
|
||||
// if (config.dataDir == newDataDir) return config;
|
||||
|
||||
// // The Codex dataDir is a little strange:
|
||||
// // If the old one is empty: The new one should not exist, so that codex creates it
|
||||
// // with the correct security permissions.
|
||||
// // If the old one does exist: We move it.
|
||||
|
||||
// if (isDir(config.dataDir)) {
|
||||
// console.log(
|
||||
// showInfoMessage(
|
||||
// "Moving Codex data folder...\n" +
|
||||
// `From: "${config.dataDir}"\n` +
|
||||
// `To: "${newDataDir}"`,
|
||||
// ),
|
||||
// );
|
||||
|
||||
// try {
|
||||
// fs.moveSync(config.dataDir, newDataDir);
|
||||
// } catch (error) {
|
||||
// console.log(
|
||||
// showErrorMessage("Error while moving dataDir: " + error.message),
|
||||
// );
|
||||
// throw error;
|
||||
// }
|
||||
// } else {
|
||||
// // Old data dir does not exist.
|
||||
// if (isDir(newDataDir)) {
|
||||
// console.log(
|
||||
// showInfoMessage(
|
||||
// "Warning: the selected data path already exists.\n" +
|
||||
// `New data path = "${newDataDir}"\n` +
|
||||
// "Codex may overwrite data in this folder.\n" +
|
||||
// "Codex will fail to start if this folder does not have the required\n" +
|
||||
// "security permissions.",
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// config.dataDir = newDataDir;
|
||||
// return config;
|
||||
// }
|
||||
}
|
||||
|
||||
editLogsDir = async () => {
|
||||
this.config.logsDir = await this.pathSelector.show(this.config.logsDir, true);
|
||||
}
|
||||
|
||||
editStorageQuota = async () => {
|
||||
this.ui.showInfoMessage("You can use: 'GB' or 'gb', etc.");
|
||||
const newQuota = await this.numberSelector.show(this.config.storageQuota, "Storage quota", true);
|
||||
if (newQuota < 100 * 1024 * 1024) {
|
||||
this.ui.showErrorMessage("Storage quote should be >= 100mb.");
|
||||
} else {
|
||||
this.config.storageQuota = newQuota;
|
||||
}
|
||||
}
|
||||
|
||||
editDiscPort = async () => {
|
||||
const newPort = await this.numberSelector.show(this.config.ports.discPort, "Discovery port", false);
|
||||
if (this.isInPortRange(newPort)) {
|
||||
this.config.ports.discPort = newPort;
|
||||
}
|
||||
}
|
||||
|
||||
editListenPort = async () => {
|
||||
const newPort = await this.numberSelector.show(this.config.ports.listenPort, "P2P listen port", false);
|
||||
if (this.isInPortRange(newPort)) {
|
||||
this.config.ports.listenPort = newPort;
|
||||
}
|
||||
}
|
||||
|
||||
editApiPort = async () => {
|
||||
const newPort = await this.numberSelector.show(this.config.ports.apiPort, "API port", false);
|
||||
if (this.isInPortRange(newPort)) {
|
||||
this.config.ports.apiPort = newPort;
|
||||
}
|
||||
}
|
||||
|
||||
isInPortRange = (number) => {
|
||||
if (number < 1024 || number > 65535) {
|
||||
this.ui.showErrorMessage("Port should be between 1024 and 65535.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
saveChangesAndExit = async () => {
|
||||
this.configService.saveConfig();
|
||||
this.ui.showInfoMessage("Configuration changes saved.");
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
discardChangesAndExit = async () => {
|
||||
this.configService.loadConfig();
|
||||
this.ui.showInfoMessage("Changes discarded.");
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
export class InstallMenu {
|
||||
constructor(uiService, config) {
|
||||
constructor(uiService, configService) {
|
||||
this.ui = uiService;
|
||||
this.config = config;
|
||||
this.config = configService.get();
|
||||
}
|
||||
|
||||
show = async () => {
|
||||
|
||||
@ -3,7 +3,7 @@ export class NumberSelector {
|
||||
this.uiService = uiService;
|
||||
}
|
||||
|
||||
showNumberSelector = async (
|
||||
show = async (
|
||||
currentValue,
|
||||
promptMessage,
|
||||
allowMetricPostfixes,
|
||||
|
||||
@ -6,14 +6,16 @@ export class PathSelector {
|
||||
this.pathMustExist = true;
|
||||
}
|
||||
|
||||
showPathSelector = async (startingPath, pathMustExist) => {
|
||||
show = async (startingPath, pathMustExist) => {
|
||||
this.running = true;
|
||||
this.startingPath = startingPath;
|
||||
this.pathMustExist = pathMustExist;
|
||||
this.roots = this.fs.getAvailableRoots();
|
||||
this.currentPath = this.splitPath(startingPath);
|
||||
if (!this.hasValidRoot(this.currentPath)) {
|
||||
this.currentPath = [roots[0]];
|
||||
}
|
||||
while (true) {
|
||||
while (this.running) {
|
||||
this.showCurrent();
|
||||
this.ui.askMultiChoice("Select an option:", [
|
||||
{
|
||||
@ -42,6 +44,8 @@ export class PathSelector {
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
return this.resultingPath;
|
||||
};
|
||||
|
||||
splitPath = (str) => {
|
||||
@ -166,4 +170,14 @@ export class PathSelector {
|
||||
if (name.length < 1) return;
|
||||
this.updateCurrentIfValidParts([...currentPath, name]);
|
||||
};
|
||||
|
||||
selectThisPath = async () => {
|
||||
this.resultingPath = this.combine(this.currentPath);
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
cancel = async () => {
|
||||
this.resultingPath = this.startingPath;
|
||||
this.running = false;
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user