mirror of
https://github.com/logos-storage/logos-storage-installer.git
synced 2026-01-05 23:13:06 +00:00
Merge pull request #6 from codex-storage/feature/codex-config
Config management
This commit is contained in:
commit
72f48bf7d6
@ -23,6 +23,8 @@
|
|||||||
"chalk": "^5.3.0",
|
"chalk": "^5.3.0",
|
||||||
"inquirer": "^9.2.12",
|
"inquirer": "^9.2.12",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"nanospinner": "^1.1.0"
|
"nanospinner": "^1.1.0",
|
||||||
|
"fs-extra": "^11.3.0",
|
||||||
|
"fs-filesystem": "^2.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
134
src/configmenu.js
Normal file
134
src/configmenu.js
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@ -10,8 +10,8 @@ import path from 'path';
|
|||||||
import mime from 'mime-types';
|
import mime from 'mime-types';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
export async function uploadFile(filePath = null, handleCommandLineOperation, showNavigationMenu) {
|
export async function uploadFile(config, filePath = null, handleCommandLineOperation, showNavigationMenu) {
|
||||||
const nodeRunning = await isNodeRunning();
|
const nodeRunning = await isNodeRunning(config);
|
||||||
if (!nodeRunning) {
|
if (!nodeRunning) {
|
||||||
console.log(showErrorMessage('Codex node is not running. Try again after starting the node'));
|
console.log(showErrorMessage('Codex node is not running. Try again after starting the node'));
|
||||||
return handleCommandLineOperation() ? process.exit(1) : showNavigationMenu();
|
return handleCommandLineOperation() ? process.exit(1) : showNavigationMenu();
|
||||||
@ -51,7 +51,7 @@ export async function uploadFile(filePath = null, handleCommandLineOperation, sh
|
|||||||
const spinner = createSpinner('Uploading file').start();
|
const spinner = createSpinner('Uploading file').start();
|
||||||
try {
|
try {
|
||||||
const result = await runCommand(
|
const result = await runCommand(
|
||||||
`curl -X POST http://localhost:8080/api/codex/v1/data ` +
|
`curl -X POST http://localhost:${config.ports.apiPort}/api/codex/v1/data ` +
|
||||||
`-H 'Content-Type: ${contentType}' ` +
|
`-H 'Content-Type: ${contentType}' ` +
|
||||||
`-H 'Content-Disposition: attachment; filename="${filename}"' ` +
|
`-H 'Content-Disposition: attachment; filename="${filename}"' ` +
|
||||||
`-w '\\n' -T "${fileToUpload}"`
|
`-w '\\n' -T "${fileToUpload}"`
|
||||||
@ -71,8 +71,8 @@ export async function uploadFile(filePath = null, handleCommandLineOperation, sh
|
|||||||
return handleCommandLineOperation() ? process.exit(0) : showNavigationMenu();
|
return handleCommandLineOperation() ? process.exit(0) : showNavigationMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function downloadFile(cid = null, handleCommandLineOperation, showNavigationMenu) {
|
export async function downloadFile(config, cid = null, handleCommandLineOperation, showNavigationMenu) {
|
||||||
const nodeRunning = await isNodeRunning();
|
const nodeRunning = await isNodeRunning(config);
|
||||||
if (!nodeRunning) {
|
if (!nodeRunning) {
|
||||||
console.log(showErrorMessage('Codex node is not running. Try again after starting the node'));
|
console.log(showErrorMessage('Codex node is not running. Try again after starting the node'));
|
||||||
return handleCommandLineOperation() ? process.exit(1) : showNavigationMenu();
|
return handleCommandLineOperation() ? process.exit(1) : showNavigationMenu();
|
||||||
@ -95,7 +95,7 @@ export async function downloadFile(cid = null, handleCommandLineOperation, showN
|
|||||||
const spinner = createSpinner('Fetching file metadata...').start();
|
const spinner = createSpinner('Fetching file metadata...').start();
|
||||||
try {
|
try {
|
||||||
// First, get the file metadata
|
// First, get the file metadata
|
||||||
const metadataResponse = await axios.post(`http://localhost:8080/api/codex/v1/data/${cidToDownload}/network`);
|
const metadataResponse = await axios.post(`http://localhost:${config.ports.apiPort}/api/codex/v1/data/${cidToDownload}/network`);
|
||||||
const { manifest } = metadataResponse.data;
|
const { manifest } = metadataResponse.data;
|
||||||
const { filename, mimetype } = manifest;
|
const { filename, mimetype } = manifest;
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ export async function downloadFile(cid = null, handleCommandLineOperation, showN
|
|||||||
spinner.start('Downloading file...');
|
spinner.start('Downloading file...');
|
||||||
|
|
||||||
// Then download the file with the correct filename
|
// Then download the file with the correct filename
|
||||||
await runCommand(`curl "http://localhost:8080/api/codex/v1/data/${cidToDownload}/network/stream" -o "${filename}"`);
|
await runCommand(`curl "http://localhost:${config.ports.apiPort}/api/codex/v1/data/${cidToDownload}/network/stream" -o "${filename}"`);
|
||||||
|
|
||||||
spinner.success();
|
spinner.success();
|
||||||
console.log(showSuccessMessage(
|
console.log(showSuccessMessage(
|
||||||
@ -144,8 +144,8 @@ export async function downloadFile(cid = null, handleCommandLineOperation, showN
|
|||||||
return handleCommandLineOperation() ? process.exit(0) : showNavigationMenu();
|
return handleCommandLineOperation() ? process.exit(0) : showNavigationMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function showLocalFiles(showNavigationMenu) {
|
export async function showLocalFiles(config, showNavigationMenu) {
|
||||||
const nodeRunning = await isNodeRunning();
|
const nodeRunning = await isNodeRunning(config);
|
||||||
if (!nodeRunning) {
|
if (!nodeRunning) {
|
||||||
console.log(showErrorMessage('Codex node is not running. Try again after starting the node'));
|
console.log(showErrorMessage('Codex node is not running. Try again after starting the node'));
|
||||||
await showNavigationMenu();
|
await showNavigationMenu();
|
||||||
@ -154,11 +154,10 @@ export async function showLocalFiles(showNavigationMenu) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const spinner = createSpinner('Fetching local files...').start();
|
const spinner = createSpinner('Fetching local files...').start();
|
||||||
const filesResponse = await runCommand('curl http://localhost:8080/api/codex/v1/data -w \'\\n\'');
|
const filesResponse = await axios.get(`http://localhost:${config.ports.apiPort}/api/codex/v1/data`);
|
||||||
|
const filesData = filesResponse.data;
|
||||||
spinner.success();
|
spinner.success();
|
||||||
|
|
||||||
const filesData = JSON.parse(filesResponse);
|
|
||||||
|
|
||||||
if (filesData.content && filesData.content.length > 0) {
|
if (filesData.content && filesData.content.length > 0) {
|
||||||
console.log(showInfoMessage(`Found ${filesData.content.length} local file(s)`));
|
console.log(showInfoMessage(`Found ${filesData.content.length} local file(s)`));
|
||||||
|
|
||||||
@ -187,6 +186,8 @@ export async function showLocalFiles(showNavigationMenu) {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
console.log(showInfoMessage("Node contains no datasets."));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(showErrorMessage(`Failed to fetch local files: ${error.message}`));
|
console.log(showErrorMessage(`Failed to fetch local files: ${error.message}`));
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { runCommand } from '../utils/command.js';
|
|||||||
import { showErrorMessage, showInfoMessage, showSuccessMessage } from '../utils/messages.js';
|
import { showErrorMessage, showInfoMessage, showSuccessMessage } from '../utils/messages.js';
|
||||||
import { checkDependencies } from '../services/nodeService.js';
|
import { checkDependencies } from '../services/nodeService.js';
|
||||||
import { saveConfig } from '../services/config.js';
|
import { saveConfig } from '../services/config.js';
|
||||||
import { getCodexRootPath, getCodexBinPath, getCodexDataDirDefaultPath, getCodexLogsPath } from '../utils/appdata.js';
|
import { getCodexRootPath, getCodexBinPath } from '../utils/appdata.js';
|
||||||
|
|
||||||
const platform = os.platform();
|
const platform = os.platform();
|
||||||
|
|
||||||
@ -68,23 +68,22 @@ export async function getCodexVersion(config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkCodexInstallation(config, showNavigationMenu) {
|
export async function installCodex(config, showNavigationMenu) {
|
||||||
const version = await getCodexVersion(config);
|
const version = await getCodexVersion(config);
|
||||||
|
|
||||||
if (version.length > 0) {
|
if (version.length > 0) {
|
||||||
console.log(chalk.green('Codex is already installed. Version:'));
|
console.log(chalk.green('Codex is already installed. Version:'));
|
||||||
console.log(chalk.green(version));
|
console.log(chalk.green(version));
|
||||||
await showNavigationMenu();
|
await showNavigationMenu();
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
console.log(chalk.cyanBright('Codex is not installed, proceeding with installation...'));
|
console.log(chalk.cyanBright('Codex is not installed, proceeding with installation...'));
|
||||||
await installCodex(config, showNavigationMenu);
|
return await performInstall(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveCodexExePathToConfig(config, codexExePath) {
|
async function saveCodexExePath(config, codexExePath) {
|
||||||
config.codexExe = codexExePath;
|
config.codexExe = codexExePath;
|
||||||
config.dataDir = getCodexDataDirDefaultPath();
|
|
||||||
config.logsDir = getCodexLogsPath();
|
|
||||||
if (!fs.existsSync(config.codexExe)) {
|
if (!fs.existsSync(config.codexExe)) {
|
||||||
console.log(showErrorMessage(`Codex executable not found in expected path: ${config.codexExe}`));
|
console.log(showErrorMessage(`Codex executable not found in expected path: ${config.codexExe}`));
|
||||||
throw new Error("Exe not found");
|
throw new Error("Exe not found");
|
||||||
@ -98,17 +97,14 @@ async function saveCodexExePathToConfig(config, codexExePath) {
|
|||||||
|
|
||||||
async function clearCodexExePathFromConfig(config) {
|
async function clearCodexExePathFromConfig(config) {
|
||||||
config.codexExe = "";
|
config.codexExe = "";
|
||||||
config.dataDir = "";
|
|
||||||
config.logsDir = "";
|
|
||||||
saveConfig(config);
|
saveConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function installCodex(config, showNavigationMenu) {
|
async function performInstall(config) {
|
||||||
const agreed = await showPrivacyDisclaimer();
|
const agreed = await showPrivacyDisclaimer();
|
||||||
if (!agreed) {
|
if (!agreed) {
|
||||||
console.log(showInfoMessage('You can find manual setup instructions at docs.codex.storage'));
|
console.log(showInfoMessage('You can find manual setup instructions at docs.codex.storage'));
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const installPath = getCodexBinPath();
|
const installPath = getCodexBinPath();
|
||||||
@ -117,7 +113,6 @@ export async function installCodex(config, showNavigationMenu) {
|
|||||||
const spinner = createSpinner('Installing Codex...').start();
|
const spinner = createSpinner('Installing Codex...').start();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (platform === 'win32') {
|
if (platform === 'win32') {
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
@ -129,7 +124,7 @@ export async function installCodex(config, showNavigationMenu) {
|
|||||||
await runCommand('curl -LO --ssl-no-revoke https://get.codex.storage/install.cmd');
|
await runCommand('curl -LO --ssl-no-revoke https://get.codex.storage/install.cmd');
|
||||||
await runCommand(`set "INSTALL_DIR=${installPath}" && "${process.cwd()}\\install.cmd"`);
|
await runCommand(`set "INSTALL_DIR=${installPath}" && "${process.cwd()}\\install.cmd"`);
|
||||||
|
|
||||||
await saveCodexExePathToConfig(config, path.join(installPath, "codex.exe"));
|
await saveCodexExePath(config, path.join(installPath, "codex.exe"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await runCommand('del /f install.cmd');
|
await runCommand('del /f install.cmd');
|
||||||
@ -171,7 +166,7 @@ export async function installCodex(config, showNavigationMenu) {
|
|||||||
await runCommand(`INSTALL_DIR="${installPath}" timeout 120 bash install.sh`);
|
await runCommand(`INSTALL_DIR="${installPath}" timeout 120 bash install.sh`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveCodexExePathToConfig(config, path.join(installPath, "codex"));
|
await saveCodexExePath(config, path.join(installPath, "codex"));
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message.includes('ECONNREFUSED') || error.message.includes('ETIMEDOUT')) {
|
if (error.message.includes('ECONNREFUSED') || error.message.includes('ETIMEDOUT')) {
|
||||||
@ -190,21 +185,28 @@ export async function installCodex(config, showNavigationMenu) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const version = await getCodexVersion(config);
|
const version = await getCodexVersion(config);
|
||||||
|
console.log(chalk.green(version));
|
||||||
|
|
||||||
console.log(showSuccessMessage(
|
console.log(showSuccessMessage(
|
||||||
'Codex is successfully installed!\n' +
|
'Codex is successfully installed!\n' +
|
||||||
`Install path: "${config.codexExe}"\n\n` +
|
`Install path: "${config.codexExe}"\n\n` +
|
||||||
`Version: ${version}`
|
'The default configuration should work for most platforms.\n' +
|
||||||
|
'Please review the configuration before starting Codex.\n'
|
||||||
));
|
));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error('Installation completed but Codex command is not available. Please restart your terminal and try again.');
|
throw new Error('Installation completed but Codex command is not available. Please restart your terminal and try again.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(showInfoMessage(
|
||||||
|
"Please review the configuration before starting Codex."
|
||||||
|
));
|
||||||
|
|
||||||
spinner.success();
|
spinner.success();
|
||||||
await showNavigationMenu();
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
spinner.error();
|
spinner.error();
|
||||||
console.log(showErrorMessage(`Failed to install Codex: ${error.message}`));
|
console.log(showErrorMessage(`Failed to install Codex: ${error.message}`));
|
||||||
await showNavigationMenu();
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,27 +43,12 @@ export async function runCodex(config, showNavigationMenu) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeAlreadyRunning = await isNodeRunning();
|
const nodeAlreadyRunning = await isNodeRunning(config);
|
||||||
|
|
||||||
if (nodeAlreadyRunning) {
|
if (nodeAlreadyRunning) {
|
||||||
console.log(showInfoMessage('A Codex node is already running.'));
|
console.log(showInfoMessage('A Codex node is already running.'));
|
||||||
await showNavigationMenu();
|
await showNavigationMenu();
|
||||||
} else {
|
} else {
|
||||||
const { discPort, listenPort } = await inquirer.prompt([
|
|
||||||
{
|
|
||||||
type: 'number',
|
|
||||||
name: 'discPort',
|
|
||||||
message: 'Enter the discovery port (default is 8090):',
|
|
||||||
default: 8090
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'number',
|
|
||||||
name: 'listenPort',
|
|
||||||
message: 'Enter the listening port (default is 8070):',
|
|
||||||
default: 8070
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let nat;
|
let nat;
|
||||||
if (platform === 'win32') {
|
if (platform === 'win32') {
|
||||||
@ -79,15 +64,19 @@ export async function runCodex(config, showNavigationMenu) {
|
|||||||
|
|
||||||
console.log(showInfoMessage(
|
console.log(showInfoMessage(
|
||||||
`Data location: ${config.dataDir}\n` +
|
`Data location: ${config.dataDir}\n` +
|
||||||
`Logs: ${logFilePath}`
|
`Logs: ${logFilePath}\n` +
|
||||||
|
`API port: ${config.ports.apiPort}`
|
||||||
));
|
));
|
||||||
|
|
||||||
const executable = config.codexExe;
|
const executable = config.codexExe;
|
||||||
const args = [
|
const args = [
|
||||||
`--data-dir="${config.dataDir}"`,
|
`--data-dir="${config.dataDir}"`,
|
||||||
|
`--log-level=DEBUG`,
|
||||||
`--log-file="${logFilePath}"`,
|
`--log-file="${logFilePath}"`,
|
||||||
`--disc-port=${discPort}`,
|
`--storage-quota="${config.storageQuota}"`,
|
||||||
`--listen-addrs=/ip4/0.0.0.0/tcp/${listenPort}`,
|
`--disc-port=${config.ports.discPort}`,
|
||||||
|
`--listen-addrs=/ip4/0.0.0.0/tcp/${config.ports.listenPort}`,
|
||||||
|
`--api-port=${config.ports.apiPort}`,
|
||||||
`--nat=${nat}`,
|
`--nat=${nat}`,
|
||||||
`--api-cors-origin="*"`,
|
`--api-cors-origin="*"`,
|
||||||
`--bootstrap-node=spr:CiUIAhIhAiJvIcA_ZwPZ9ugVKDbmqwhJZaig5zKyLiuaicRcCGqLEgIDARo8CicAJQgCEiECIm8hwD9nA9n26BUoNuarCEllqKDnMrIuK5qJxFwIaosQ3d6esAYaCwoJBJ_f8zKRAnU6KkYwRAIgM0MvWNJL296kJ9gWvfatfmVvT-A7O2s8Mxp8l9c8EW0CIC-h-H-jBVSgFjg3Eny2u33qF7BDnWFzo7fGfZ7_qc9P`
|
`--bootstrap-node=spr:CiUIAhIhAiJvIcA_ZwPZ9ugVKDbmqwhJZaig5zKyLiuaicRcCGqLEgIDARo8CicAJQgCEiECIm8hwD9nA9n26BUoNuarCEllqKDnMrIuK5qJxFwIaosQ3d6esAYaCwoJBJ_f8zKRAnU6KkYwRAIgM0MvWNJL296kJ9gWvfatfmVvT-A7O2s8Mxp8l9c8EW0CIC-h-H-jBVSgFjg3Eny2u33qF7BDnWFzo7fGfZ7_qc9P`
|
||||||
@ -108,7 +97,7 @@ export async function runCodex(config, showNavigationMenu) {
|
|||||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/api/codex/v1/debug/info');
|
const response = await axios.get(`http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
// Check if wallet exists
|
// Check if wallet exists
|
||||||
try {
|
try {
|
||||||
@ -126,7 +115,7 @@ export async function runCodex(config, showNavigationMenu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start periodic logging
|
// Start periodic logging
|
||||||
const stopLogging = await startPeriodicLogging();
|
const stopLogging = await startPeriodicLogging(config);
|
||||||
|
|
||||||
nodeProcess.on('exit', () => {
|
nodeProcess.on('exit', () => {
|
||||||
stopLogging();
|
stopLogging();
|
||||||
@ -260,13 +249,13 @@ async function showNodeDetails(data, showNavigationMenu) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkNodeStatus(showNavigationMenu) {
|
export async function checkNodeStatus(config, showNavigationMenu) {
|
||||||
try {
|
try {
|
||||||
const nodeRunning = await isNodeRunning();
|
const nodeRunning = await isNodeRunning(config);
|
||||||
|
|
||||||
if (nodeRunning) {
|
if (nodeRunning) {
|
||||||
const spinner = createSpinner('Checking node status...').start();
|
const spinner = createSpinner('Checking node status...').start();
|
||||||
const response = await runCommand('curl http://localhost:8080/api/codex/v1/debug/info');
|
const response = await runCommand(`curl http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`);
|
||||||
spinner.success();
|
spinner.success();
|
||||||
|
|
||||||
const data = JSON.parse(response);
|
const data = JSON.parse(response);
|
||||||
|
|||||||
56
src/main.js
56
src/main.js
@ -6,10 +6,11 @@ import boxen from 'boxen';
|
|||||||
import { ASCII_ART } from './constants/ascii.js';
|
import { ASCII_ART } from './constants/ascii.js';
|
||||||
import { handleCommandLineOperation, parseCommandLineArgs } from './cli/commandParser.js';
|
import { handleCommandLineOperation, parseCommandLineArgs } from './cli/commandParser.js';
|
||||||
import { uploadFile, downloadFile, showLocalFiles } from './handlers/fileHandlers.js';
|
import { uploadFile, downloadFile, showLocalFiles } from './handlers/fileHandlers.js';
|
||||||
import { checkCodexInstallation, installCodex, uninstallCodex } from './handlers/installationHandlers.js';
|
import { installCodex, uninstallCodex } from './handlers/installationHandlers.js';
|
||||||
import { runCodex, checkNodeStatus } from './handlers/nodeHandlers.js';
|
import { runCodex, checkNodeStatus } from './handlers/nodeHandlers.js';
|
||||||
import { showInfoMessage } from './utils/messages.js';
|
import { showInfoMessage } from './utils/messages.js';
|
||||||
import { loadConfig } from './services/config.js';
|
import { loadConfig } from './services/config.js';
|
||||||
|
import { showConfigMenu } from './configmenu.js';
|
||||||
|
|
||||||
async function showNavigationMenu() {
|
async function showNavigationMenu() {
|
||||||
console.log('\n')
|
console.log('\n')
|
||||||
@ -78,57 +79,62 @@ export async function main() {
|
|||||||
message: 'Select an option:',
|
message: 'Select an option:',
|
||||||
choices: [
|
choices: [
|
||||||
'1. Download and install Codex',
|
'1. Download and install Codex',
|
||||||
'2. Run Codex node',
|
'2. Edit Codex configuration',
|
||||||
'3. Check node status',
|
'3. Run Codex node',
|
||||||
'4. Upload a file',
|
'4. Check node status',
|
||||||
'5. Download a file',
|
'5. Upload a file',
|
||||||
'6. Show local data',
|
'6. Download a file',
|
||||||
'7. Uninstall Codex node',
|
'7. Show local data',
|
||||||
'8. Submit feedback',
|
'8. Uninstall Codex node',
|
||||||
'9. Exit'
|
'9. Submit feedback',
|
||||||
|
'10. Exit'
|
||||||
],
|
],
|
||||||
pageSize: 9,
|
pageSize: 10,
|
||||||
loop: true
|
loop: true
|
||||||
}
|
}
|
||||||
]).catch(() => {
|
]).catch(() => {
|
||||||
handleExit();
|
handleExit();
|
||||||
return { choice: '9' };
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (choice.startsWith('9')) {
|
|
||||||
handleExit();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (choice.split('.')[0]) {
|
switch (choice.split('.')[0]) {
|
||||||
case '1':
|
case '1':
|
||||||
await checkCodexInstallation(config, showNavigationMenu);
|
const installed = await installCodex(config, showNavigationMenu);
|
||||||
|
if (installed) {
|
||||||
|
await showConfigMenu(config);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case '2':
|
case '2':
|
||||||
|
await showConfigMenu(config);
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
await runCodex(config, showNavigationMenu);
|
await runCodex(config, showNavigationMenu);
|
||||||
return;
|
return;
|
||||||
case '3':
|
|
||||||
await checkNodeStatus(showNavigationMenu);
|
|
||||||
break;
|
|
||||||
case '4':
|
case '4':
|
||||||
await uploadFile(null, handleCommandLineOperation, showNavigationMenu);
|
await checkNodeStatus(config, showNavigationMenu);
|
||||||
break;
|
break;
|
||||||
case '5':
|
case '5':
|
||||||
await downloadFile(null, handleCommandLineOperation, showNavigationMenu);
|
await uploadFile(config, null, handleCommandLineOperation, showNavigationMenu);
|
||||||
break;
|
break;
|
||||||
case '6':
|
case '6':
|
||||||
await showLocalFiles(showNavigationMenu);
|
await downloadFile(config, null, handleCommandLineOperation, showNavigationMenu);
|
||||||
break;
|
break;
|
||||||
case '7':
|
case '7':
|
||||||
await uninstallCodex(config, showNavigationMenu);
|
await showLocalFiles(config, showNavigationMenu);
|
||||||
break;
|
break;
|
||||||
case '8':
|
case '8':
|
||||||
|
await uninstallCodex(config, showNavigationMenu);
|
||||||
|
break;
|
||||||
|
case '9':
|
||||||
const { exec } = await import('child_process');
|
const { exec } = await import('child_process');
|
||||||
const url = 'https://docs.google.com/forms/d/1U21xp6shfDkJWzJSKHhUjwIE7fsYk94gmLUKAbxUMcw/edit';
|
const url = 'https://docs.google.com/forms/d/1U21xp6shfDkJWzJSKHhUjwIE7fsYk94gmLUKAbxUMcw/edit';
|
||||||
const command = process.platform === 'win32' ? `start ${url}` : process.platform === 'darwin' ? `open ${url}` : `xdg-open ${url}`;
|
const command = process.platform === 'win32' ? `start ${url}` : process.platform === 'darwin' ? `open ${url}` : `xdg-open ${url}`;
|
||||||
exec(command);
|
exec(command);
|
||||||
console.log(showInfoMessage('Opening feedback form in your browser...'));
|
console.log(showInfoMessage('Opening feedback form in your browser...'));
|
||||||
break;
|
break;
|
||||||
|
case '10':
|
||||||
|
handleExit();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('\n');
|
console.log('\n');
|
||||||
|
|||||||
@ -1,20 +1,19 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { getAppDataDir } from '../utils/appdata.js';
|
import { getAppDataDir } from '../utils/appdata.js';
|
||||||
|
import { getCodexDataDirDefaultPath, getCodexLogsDefaultPath } from '../utils/appdata.js';
|
||||||
|
|
||||||
const defaultConfig = {
|
const defaultConfig = {
|
||||||
codexExe: "",
|
codexExe: "",
|
||||||
|
// User-selected config options:
|
||||||
// TODO:
|
dataDir: getCodexDataDirDefaultPath(),
|
||||||
// Save user-selected config options. Use these when starting Codex.
|
logsDir: getCodexLogsDefaultPath(),
|
||||||
dataDir: "",
|
storageQuota: 8 * 1024 * 1024 * 1024,
|
||||||
logsDir: ""
|
ports: {
|
||||||
// storageQuota: 0,
|
discPort: 8090,
|
||||||
// ports: {
|
listenPort: 8070,
|
||||||
// discPort: 8090,
|
apiPort: 8080
|
||||||
// listenPort: 8070,
|
}
|
||||||
// apiPort: 8080
|
|
||||||
// }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getConfigFilename() {
|
function getConfigFilename() {
|
||||||
|
|||||||
@ -21,9 +21,9 @@ export async function getWalletAddress() {
|
|||||||
return currentWallet;
|
return currentWallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isNodeRunning() {
|
export async function isNodeRunning(config) {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/api/codex/v1/debug/info');
|
const response = await axios.get(`http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`);
|
||||||
return response.status === 200;
|
return response.status === 200;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
@ -105,12 +105,12 @@ export async function checkDependencies() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startPeriodicLogging() {
|
export async function startPeriodicLogging(config) {
|
||||||
const FIFTEEN_MINUTES = 15 * 60 * 1000; // 15 minutes in milliseconds
|
const FIFTEEN_MINUTES = 15 * 60 * 1000; // 15 minutes in milliseconds
|
||||||
|
|
||||||
const logNodeInfo = async () => {
|
const logNodeInfo = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/api/codex/v1/debug/info');
|
const response = await axios.get(`http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
await logToSupabase(response.data);
|
await logToSupabase(response.data);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export function getCodexDataDirDefaultPath() {
|
|||||||
return path.join(appData("codex"), "datadir");
|
return path.join(appData("codex"), "datadir");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCodexLogsPath() {
|
export function getCodexLogsDefaultPath() {
|
||||||
return ensureExists(path.join(appData("codex"), "logs"));
|
return ensureExists(path.join(appData("codex"), "logs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
src/utils/numberSelector.js
Normal file
47
src/utils/numberSelector.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import inquirer from 'inquirer';
|
||||||
|
|
||||||
|
function getMetricsMult(valueStr, allowMetricPostfixes) {
|
||||||
|
if (!allowMetricPostfixes) return 1;
|
||||||
|
const lower = valueStr.toLowerCase();
|
||||||
|
if (lower.endsWith("tb") || lower.endsWith("t")) return Math.pow(1024, 4);
|
||||||
|
if (lower.endsWith("gb") || lower.endsWith("g")) return Math.pow(1024, 3);
|
||||||
|
if (lower.endsWith("mb") || lower.endsWith("m")) return Math.pow(1024, 2);
|
||||||
|
if (lower.endsWith("kb") || lower.endsWith("k")) return Math.pow(1024, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNumericValue(valueStr) {
|
||||||
|
try {
|
||||||
|
const num = valueStr.match(/\d+/g);
|
||||||
|
const result = parseInt(num);
|
||||||
|
if (isNaN(result) || !isFinite(result)) {
|
||||||
|
throw new Error("Invalid input received.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Failed to parse input: " + error.message);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function promptForValueStr(promptMessage) {
|
||||||
|
const response = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: 'input',
|
||||||
|
name: 'valueStr',
|
||||||
|
message: promptMessage
|
||||||
|
}]);
|
||||||
|
return response.valueStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function showNumberSelector(currentValue, promptMessage, allowMetricPostfixes) {
|
||||||
|
try {
|
||||||
|
var valueStr = await promptForValueStr(promptMessage);
|
||||||
|
valueStr = valueStr.replaceAll(" ", "");
|
||||||
|
const mult = getMetricsMult(valueStr, allowMetricPostfixes);
|
||||||
|
const value = getNumericValue(valueStr);
|
||||||
|
return value * mult;
|
||||||
|
} catch {
|
||||||
|
return currentValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import inquirer from 'inquirer';
|
|||||||
import boxen from 'boxen';
|
import boxen from 'boxen';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import { filesystemSync } from 'fs-filesystem';
|
||||||
|
|
||||||
function showMsg(msg) {
|
function showMsg(msg) {
|
||||||
console.log(boxen(chalk.white(msg), {
|
console.log(boxen(chalk.white(msg), {
|
||||||
@ -14,13 +15,73 @@ function showMsg(msg) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAvailableRoots() {
|
||||||
|
const devices = filesystemSync();
|
||||||
|
var mountPoints = [];
|
||||||
|
Object.keys(devices).forEach(function(key) {
|
||||||
|
var val = devices[key];
|
||||||
|
val.volumes.forEach(function(volume) {
|
||||||
|
mountPoints.push(volume.mountPoint);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mountPoints.length < 1) {
|
||||||
|
throw new Error("Failed to detect file system devices.");
|
||||||
|
}
|
||||||
|
return mountPoints;
|
||||||
|
}
|
||||||
|
|
||||||
function splitPath(str) {
|
function splitPath(str) {
|
||||||
return str.replaceAll("\\", "/").split("/");
|
return str.replaceAll("\\", "/").split("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function dropEmptyParts(parts) {
|
||||||
|
var result = [];
|
||||||
|
parts.forEach(function(part) {
|
||||||
|
if (part.length > 0) {
|
||||||
|
result.push(part);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function combine(parts) {
|
||||||
|
const toJoin = dropEmptyParts(parts);
|
||||||
|
if (toJoin.length == 1) return toJoin[0];
|
||||||
|
return path.join(...toJoin);
|
||||||
|
}
|
||||||
|
|
||||||
|
function combineWith(parts, extra) {
|
||||||
|
const toJoin = dropEmptyParts(parts);
|
||||||
|
if (toJoin.length == 1) return path.join(toJoin[0], extra);
|
||||||
|
return path.join(...toJoin, extra);
|
||||||
|
}
|
||||||
|
|
||||||
function showCurrent(currentPath) {
|
function showCurrent(currentPath) {
|
||||||
const len = currentPath.length;
|
const len = currentPath.length;
|
||||||
showMsg(`Current path: [${len}]\n` + path.join(...currentPath));
|
showMsg(`Current path: [${len}]\n` + combine(currentPath));
|
||||||
|
|
||||||
|
if (len < 2) {
|
||||||
|
showMsg(
|
||||||
|
'Warning - Known issue:\n' +
|
||||||
|
'Path selection does not work in root paths on some platforms.\n' +
|
||||||
|
'Use "Enter path" or "Create new folder" to navigate and create folders\n' +
|
||||||
|
'if this is the case for you.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasValidRoot(roots, checkPath) {
|
||||||
|
if (checkPath.length < 1) return false;
|
||||||
|
var result = false;
|
||||||
|
roots.forEach(function(root) {
|
||||||
|
if (root.toLowerCase() == checkPath[0].toLowerCase()) {
|
||||||
|
console.log("valid root: " + combine(checkPath));
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!result) console.log("invalid root: " + combine(checkPath));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showMain(currentPath) {
|
async function showMain(currentPath) {
|
||||||
@ -34,10 +95,9 @@ async function showMain(currentPath) {
|
|||||||
'1. Enter path',
|
'1. Enter path',
|
||||||
'2. Go up one',
|
'2. Go up one',
|
||||||
'3. Go down one',
|
'3. Go down one',
|
||||||
'4. Check path exists',
|
'4. Create new folder here',
|
||||||
'5. Create new folder here',
|
'5. Select this path',
|
||||||
'6. Select this path',
|
'6. Cancel'
|
||||||
'7. Cancel'
|
|
||||||
],
|
],
|
||||||
pageSize: 6,
|
pageSize: 6,
|
||||||
loop: true
|
loop: true
|
||||||
@ -50,41 +110,50 @@ async function showMain(currentPath) {
|
|||||||
return choice;
|
return choice;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function selectPath() {
|
export async function showPathSelector(startingPath, pathMustExist) {
|
||||||
var currentPath = splitPath(process.cwd());
|
const roots = getAvailableRoots();
|
||||||
|
var currentPath = splitPath(startingPath);
|
||||||
|
if (!hasValidRoot(roots, currentPath)) {
|
||||||
|
currentPath = [roots[0]];
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const choice = await showMain(currentPath);
|
const choice = await showMain(currentPath);
|
||||||
|
|
||||||
|
var newCurrentPath = currentPath;
|
||||||
switch (choice.split('.')[0]) {
|
switch (choice.split('.')[0]) {
|
||||||
case '1':
|
case '1':
|
||||||
currentPath = await enterPath();
|
newCurrentPath = await enterPath(currentPath, pathMustExist);
|
||||||
break;
|
break;
|
||||||
case '2':
|
case '2':
|
||||||
currentPath = upOne(currentPath);
|
newCurrentPath = upOne(currentPath);
|
||||||
break;
|
break;
|
||||||
case '3':
|
case '3':
|
||||||
currentPath = await downOne(currentPath);
|
newCurrentPath = await downOne(currentPath);
|
||||||
break;
|
break;
|
||||||
case '4':
|
case '4':
|
||||||
await checkExists(currentPath);
|
newCurrentPath = await createSubDir(currentPath, pathMustExist);
|
||||||
break;
|
break;
|
||||||
case '5':
|
case '5':
|
||||||
currentPath = await createSubDir(currentPath);
|
if (pathMustExist && !isDir(combine(currentPath))) {
|
||||||
break;
|
|
||||||
case '6':
|
|
||||||
if (!isDir(currentPath)) {
|
|
||||||
console.log("Current path does not exist.");
|
console.log("Current path does not exist.");
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
return currentPath;
|
return combine(currentPath);
|
||||||
}
|
}
|
||||||
case '7':
|
case '6':
|
||||||
return "";
|
return combine(currentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasValidRoot(roots, newCurrentPath)) {
|
||||||
|
currentPath = newCurrentPath;
|
||||||
|
} else {
|
||||||
|
console.log("Selected path has no valid root.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function enterPath() {
|
async function enterPath(currentPath, pathMustExist) {
|
||||||
const response = await inquirer.prompt([
|
const response = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@ -92,6 +161,11 @@ async function enterPath() {
|
|||||||
message: 'Enter Path:'
|
message: 'Enter Path:'
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
const newPath = response.path;
|
||||||
|
if (pathMustExist && !isDir(newPath)) {
|
||||||
|
console.log("The entered path does not exist.");
|
||||||
|
return currentPath;
|
||||||
|
}
|
||||||
return splitPath(response.path);
|
return splitPath(response.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,22 +173,28 @@ function upOne(currentPath) {
|
|||||||
return currentPath.slice(0, currentPath.length - 1);
|
return currentPath.slice(0, currentPath.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDir(dir) {
|
export function isDir(dir) {
|
||||||
return fs.lstatSync(dir).isDirectory();
|
try {
|
||||||
|
return fs.lstatSync(dir).isDirectory();
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSubDir(currentPath, entry) {
|
function isSubDir(currentPath, entry) {
|
||||||
const newPath = path.join(...currentPath, entry);
|
const newPath = combineWith(currentPath, entry);
|
||||||
return isDir(newPath);
|
return isDir(newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubDirOptions(currentPath) {
|
function getSubDirOptions(currentPath) {
|
||||||
const entries = fs.readdirSync(path.join(...currentPath));
|
const fullPath = combine(currentPath);
|
||||||
|
const entries = fs.readdirSync(fullPath);
|
||||||
var result = [];
|
var result = [];
|
||||||
var counter = 1;
|
var counter = 1;
|
||||||
entries.forEach(function(entry) {
|
entries.forEach(function(entry) {
|
||||||
if (isSubDir(currentPath, entry)) {
|
if (isSubDir(currentPath, entry)) {
|
||||||
result.push(counter + ". " + entry);
|
result.push(counter + '. ' + entry);
|
||||||
|
counter = counter + 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
@ -140,19 +220,11 @@ async function downOne(currentPath) {
|
|||||||
return currentPath;
|
return currentPath;
|
||||||
});
|
});
|
||||||
|
|
||||||
const subDir = choice.slice(3);
|
const subDir = choice.split('. ')[1];
|
||||||
return [...currentPath, subDir];
|
return [...currentPath, subDir];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkExists(currentPath) {
|
async function createSubDir(currentPath, pathMustExist) {
|
||||||
if (!isDir(path.join(...currentPath))) {
|
|
||||||
console.log("Current path does not exist.");
|
|
||||||
} else{
|
|
||||||
console.log("Current path exists.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createSubDir(currentPath) {
|
|
||||||
const response = await inquirer.prompt([
|
const response = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
@ -163,7 +235,9 @@ async function createSubDir(currentPath) {
|
|||||||
const name = response.name;
|
const name = response.name;
|
||||||
if (name.length < 1) return;
|
if (name.length < 1) return;
|
||||||
|
|
||||||
const fullDir = path.join(...currentPath, name);
|
const fullDir = combineWith(currentPath, name);
|
||||||
fs.mkdirSync(fullDir);
|
if (pathMustExist && !isDir(fullDir)) {
|
||||||
|
fs.mkdirSync(fullDir);
|
||||||
|
}
|
||||||
return [...currentPath, name];
|
return [...currentPath, name];
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user