mirror of
https://github.com/logos-storage/logos-storage-installer.git
synced 2026-01-05 23:13:06 +00:00
Merge branch 'master' into feature/upload-download
# Conflicts: # src/handlers/installationHandlers.js # src/handlers/nodeHandlers.js
This commit is contained in:
commit
b3c7a62a3f
@ -115,6 +115,46 @@ async function clearCodexExePathFromConfig(config) {
|
|||||||
saveConfig(config);
|
saveConfig(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function configureShellPath(installPath) {
|
||||||
|
try {
|
||||||
|
const homedir = os.homedir();
|
||||||
|
let shellConfigFile;
|
||||||
|
let exportLine = `export PATH="${installPath}:$PATH"`;
|
||||||
|
|
||||||
|
if (platform === 'win32') {
|
||||||
|
// For Windows, update the User PATH environment variable
|
||||||
|
const currentPath = process.env.PATH || '';
|
||||||
|
if (!currentPath.includes(installPath)) {
|
||||||
|
await runCommand(`setx PATH "${installPath};%PATH%"`);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// For Unix-like systems (macOS and Linux)
|
||||||
|
if (platform === 'darwin') {
|
||||||
|
// Check for zsh first (default on modern macOS)
|
||||||
|
if (fs.existsSync(path.join(homedir, '.zshrc'))) {
|
||||||
|
shellConfigFile = path.join(homedir, '.zshrc');
|
||||||
|
} else {
|
||||||
|
shellConfigFile = path.join(homedir, '.bash_profile');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Linux
|
||||||
|
shellConfigFile = path.join(homedir, '.bashrc');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if PATH is already configured
|
||||||
|
const fileContent = fs.existsSync(shellConfigFile) ? fs.readFileSync(shellConfigFile, 'utf8') : '';
|
||||||
|
if (!fileContent.includes(installPath)) {
|
||||||
|
fs.appendFileSync(shellConfigFile, `\n${exportLine}\n`);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(showErrorMessage(`Failed to configure PATH: ${error.message}`));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function performInstall(config) {
|
async function performInstall(config) {
|
||||||
const agreed = await showPrivacyDisclaimer();
|
const agreed = await showPrivacyDisclaimer();
|
||||||
if (!agreed) {
|
if (!agreed) {
|
||||||
@ -185,34 +225,114 @@ async function performInstall(config) {
|
|||||||
};
|
};
|
||||||
die if $@;
|
die if $@;
|
||||||
'`;
|
'`;
|
||||||
await runCommand(timeoutCommand);
|
await runCommand(timeoutCommand);
|
||||||
} else {
|
} else {
|
||||||
await runCommand(
|
await runCommand(`INSTALL_DIR="${installPath}" timeout 120 bash install.sh`);
|
||||||
`INSTALL_DIR="${installPath}" timeout 120 bash install.sh`,
|
}
|
||||||
);
|
|
||||||
|
await saveCodexExePath(config, path.join(installPath, "codex"));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message.includes('ECONNREFUSED') || error.message.includes('ETIMEDOUT')) {
|
||||||
|
throw new Error('Installation failed. Please check your internet connection and try again.');
|
||||||
|
} else if (error.message.includes('Permission denied')) {
|
||||||
|
throw new Error('Permission denied. Please try running the command with sudo.');
|
||||||
|
} else if (error.message.includes('timeout')) {
|
||||||
|
throw new Error('Installation is taking longer than expected. Please try running with sudo.');
|
||||||
|
} else {
|
||||||
|
throw new Error('Installation failed. Please try running with sudo if you haven\'t already.');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await runCommand('rm -f install.sh').catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const version = await getCodexVersion(config);
|
||||||
|
console.log(chalk.green(version));
|
||||||
|
|
||||||
|
// Configure shell PATH
|
||||||
|
const pathConfigured = await configureShellPath(installPath);
|
||||||
|
spinner.success();
|
||||||
|
|
||||||
|
if (pathConfigured) {
|
||||||
|
console.log(boxen(
|
||||||
|
chalk.green('Codex is installed successfully!\n\n') +
|
||||||
|
chalk.cyan('Current configuration:\n') +
|
||||||
|
`${chalk.white('Data path')} = ${config.dataDir}\n` +
|
||||||
|
`${chalk.white('Logs path')} = ${config.logsDir}\n` +
|
||||||
|
`${chalk.white('Storage quota')} = ${config.storageQuota} Bytes\n` +
|
||||||
|
`${chalk.white('Discovery port')} = ${config.ports.discPort}\n` +
|
||||||
|
`${chalk.white('P2P Listen port')} = ${config.ports.listenPort}\n` +
|
||||||
|
`${chalk.white('API Port')} = ${config.ports.apiPort}\n\n` +
|
||||||
|
chalk.gray('You can modify the above configuration from the main menu.'),
|
||||||
|
{
|
||||||
|
padding: 1,
|
||||||
|
margin: 1,
|
||||||
|
borderStyle: 'round',
|
||||||
|
borderColor: 'green',
|
||||||
|
title: '✅ Installation Complete',
|
||||||
|
titleAlignment: 'center'
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
const { choice } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: 'list',
|
||||||
|
name: 'choice',
|
||||||
|
message: 'What would you like to do?',
|
||||||
|
choices: [
|
||||||
|
'1. Return to main menu',
|
||||||
|
'2. Exit',
|
||||||
|
],
|
||||||
|
pageSize: 2,
|
||||||
|
loop: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
switch (choice.split('.')[0].trim()) {
|
||||||
|
case '1':
|
||||||
|
await showNavigationMenu();
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(showInfoMessage(
|
||||||
|
'Codex is installed but PATH configuration failed.\n' +
|
||||||
|
`Please manually add "${installPath}" to your PATH.\n`
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Installation completed but Codex command is not available. Please restart your terminal and try again.');
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveCodexExePath(config, path.join(installPath, "codex"));
|
console.log(showInfoMessage(
|
||||||
} catch (error) {
|
"Please review the configuration before starting Codex."
|
||||||
if (
|
));
|
||||||
error.message.includes("ECONNREFUSED") ||
|
|
||||||
error.message.includes("ETIMEDOUT")
|
return true;
|
||||||
) {
|
} catch (error) {
|
||||||
throw new Error(
|
spinner.error();
|
||||||
"Installation failed. Please check your internet connection and try again.",
|
console.log(showErrorMessage(`Failed to install Codex: ${error.message}`));
|
||||||
);
|
return false;
|
||||||
} else if (error.message.includes("Permission denied")) {
|
}
|
||||||
throw new Error(
|
}
|
||||||
"Permission denied. Please try running the command with sudo.",
|
|
||||||
);
|
function removeDir(dir) {
|
||||||
} else if (error.message.includes("timeout")) {
|
fs.rmSync(dir, { recursive: true, force: true });
|
||||||
throw new Error(
|
}
|
||||||
"Installation is taking longer than expected. Please try running with sudo.",
|
|
||||||
);
|
export async function uninstallCodex(config, showNavigationMenu) {
|
||||||
} else {
|
const { confirm } = await inquirer.prompt([
|
||||||
throw new Error(
|
{
|
||||||
"Installation failed. Please try running with sudo if you haven't already.",
|
type: 'confirm',
|
||||||
);
|
name: 'confirm',
|
||||||
|
message: chalk.yellow(
|
||||||
|
'⚠️ Are you sure you want to uninstall Codex? This action cannot be undone. \n' +
|
||||||
|
'All data stored in the local Codex node will be deleted as well.'
|
||||||
|
),
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await runCommand("rm -f install.sh").catch(() => {});
|
await runCommand("rm -f install.sh").catch(() => {});
|
||||||
|
|||||||
@ -1,349 +1,296 @@
|
|||||||
import path from "path";
|
import path from 'path';
|
||||||
import { createSpinner } from "nanospinner";
|
import { createSpinner } from 'nanospinner';
|
||||||
import { runCommand } from "../utils/command.js";
|
import { runCommand } from '../utils/command.js';
|
||||||
import {
|
import { showErrorMessage, showInfoMessage, showSuccessMessage } from '../utils/messages.js';
|
||||||
showErrorMessage,
|
import { isNodeRunning, isCodexInstalled, startPeriodicLogging, getWalletAddress, setWalletAddress } from '../services/nodeService.js';
|
||||||
showInfoMessage,
|
import inquirer from 'inquirer';
|
||||||
showSuccessMessage,
|
import boxen from 'boxen';
|
||||||
} from "../utils/messages.js";
|
import chalk from 'chalk';
|
||||||
import {
|
import os from 'os';
|
||||||
isNodeRunning,
|
import { exec } from 'child_process';
|
||||||
isCodexInstalled,
|
import axios from 'axios';
|
||||||
startPeriodicLogging,
|
|
||||||
getWalletAddress,
|
|
||||||
setWalletAddress,
|
|
||||||
} from "../services/nodeService.js";
|
|
||||||
import inquirer from "inquirer";
|
|
||||||
import boxen from "boxen";
|
|
||||||
import chalk from "chalk";
|
|
||||||
import os from "os";
|
|
||||||
import { exec } from "child_process";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
const platform = os.platform();
|
const platform = os.platform();
|
||||||
|
|
||||||
async function promptForWalletAddress() {
|
async function promptForWalletAddress() {
|
||||||
const { wallet } = await inquirer.prompt([
|
const { wallet } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: "input",
|
type: 'input',
|
||||||
name: "wallet",
|
name: 'wallet',
|
||||||
message:
|
message: 'Please enter your ERC20 wallet address (or press enter to skip):',
|
||||||
"Please enter your ERC20 wallet address (or press enter to skip):",
|
validate: (input) => {
|
||||||
validate: (input) => {
|
if (!input) return true; // Allow empty input
|
||||||
if (!input) return true; // Allow empty input
|
if (/^0x[a-fA-F0-9]{40}$/.test(input)) return true;
|
||||||
if (/^0x[a-fA-F0-9]{40}$/.test(input)) return true;
|
return 'Please enter a valid ERC20 wallet address (0x followed by 40 hexadecimal characters) or press enter to skip';
|
||||||
return "Please enter a valid ERC20 wallet address (0x followed by 40 hexadecimal characters) or press enter to skip";
|
}
|
||||||
},
|
}
|
||||||
},
|
]);
|
||||||
]);
|
return wallet || null;
|
||||||
return wallet || null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentLogFile(config) {
|
function getCurrentLogFile(config) {
|
||||||
const timestamp = new Date()
|
const timestamp = new Date().toISOString()
|
||||||
.toISOString()
|
.replaceAll(":", "-")
|
||||||
.replaceAll(":", "-")
|
.replaceAll(".", "-");
|
||||||
.replaceAll(".", "-");
|
return path.join(config.logsDir, `codex_${timestamp}.log`);
|
||||||
return path.join(config.logsDir, `codex_${timestamp}.log`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runCodex(config, showNavigationMenu) {
|
export async function runCodex(config, showNavigationMenu) {
|
||||||
const isInstalled = await isCodexInstalled(config);
|
const isInstalled = await isCodexInstalled(config);
|
||||||
if (!isInstalled) {
|
if (!isInstalled) {
|
||||||
console.log(
|
console.log(showErrorMessage('Codex is not installed. Please install Codex first using option 1 from the main menu.'));
|
||||||
showErrorMessage(
|
await showNavigationMenu();
|
||||||
"Codex is not installed. Please install Codex first using option 1 from the main menu.",
|
return;
|
||||||
),
|
}
|
||||||
);
|
|
||||||
await showNavigationMenu();
|
const nodeAlreadyRunning = await isNodeRunning(config);
|
||||||
return;
|
|
||||||
}
|
if (nodeAlreadyRunning) {
|
||||||
|
console.log(showInfoMessage('A Codex node is already running.'));
|
||||||
const nodeAlreadyRunning = await isNodeRunning(config);
|
await showNavigationMenu();
|
||||||
|
} else {
|
||||||
if (nodeAlreadyRunning) {
|
try {
|
||||||
console.log(showInfoMessage("A Codex node is already running."));
|
let nat;
|
||||||
await showNavigationMenu();
|
if (platform === 'win32') {
|
||||||
} else {
|
const result = await runCommand('for /f "delims=" %a in (\'curl -s --ssl-reqd ip.codex.storage\') do @echo %a');
|
||||||
try {
|
nat = result.trim();
|
||||||
let nat;
|
} else {
|
||||||
if (platform === "win32") {
|
nat = await runCommand('curl -s https://ip.codex.storage');
|
||||||
const result = await runCommand(
|
}
|
||||||
"for /f \"delims=\" %a in ('curl -s --ssl-reqd ip.codex.storage') do @echo %a",
|
|
||||||
);
|
if (config.dataDir.length < 1) throw new Error("Missing config: dataDir");
|
||||||
nat = result.trim();
|
if (config.logsDir.length < 1) throw new Error("Missing config: logsDir");
|
||||||
} else {
|
const logFilePath = getCurrentLogFile(config);
|
||||||
nat = await runCommand("curl -s https://ip.codex.storage");
|
|
||||||
}
|
console.log(showInfoMessage(
|
||||||
|
`Data location: ${config.dataDir}\n` +
|
||||||
if (config.dataDir.length < 1) throw new Error("Missing config: dataDir");
|
`Logs: ${logFilePath}\n` +
|
||||||
if (config.logsDir.length < 1) throw new Error("Missing config: logsDir");
|
`API port: ${config.ports.apiPort}`
|
||||||
const logFilePath = getCurrentLogFile(config);
|
));
|
||||||
|
|
||||||
console.log(
|
const executable = config.codexExe;
|
||||||
showInfoMessage(
|
const args = [
|
||||||
`Data location: ${config.dataDir}\n` +
|
`--data-dir="${config.dataDir}"`,
|
||||||
`Logs: ${logFilePath}\n` +
|
`--api-cors-origin="*"`,
|
||||||
`API port: ${config.ports.apiPort}`,
|
`--storage-quota=11811160064`,
|
||||||
),
|
`--bootstrap-node=spr:CiUIAhIhAiJvIcA_ZwPZ9ugVKDbmqwhJZaig5zKyLiuaicRcCGqLEgIDARo8CicAJQgCEiECIm8hwD9nA9n26BUoNuarCEllqKDnMrIuK5qJxFwIaosQ3d6esAYaCwoJBJ_f8zKRAnU6KkYwRAIgM0MvWNJL296kJ9gWvfatfmVvT-A7O2s8Mxp8l9c8EW0CIC-h-H-jBVSgFjg3Eny2u33qF7BDnWFzo7fGfZ7_qc9P`,
|
||||||
);
|
`--bootstrap-node=spr:CiUIAhIhAyUvcPkKoGE7-gh84RmKIPHJPdsX5Ugm_IHVJgF-Mmu_EgIDARo8CicAJQgCEiEDJS9w-QqgYTv6CHzhGYog8ck92xflSCb8gdUmAX4ya78QoemesAYaCwoJBES39Q2RAnVOKkYwRAIgLi3rouyaZFS_Uilx8k99ySdQCP1tsmLR21tDb9p8LcgCIG30o5YnEooQ1n6tgm9fCT7s53k6XlxyeSkD_uIO9mb3`,
|
||||||
|
`--bootstrap-node=spr:CiUIAhIhA7E4DEMer8nUOIUSaNPA4z6x0n9Xaknd28Cfw9S2-cCeEgIDARo8CicAJQgCEiEDsTgMQx6vydQ4hRJo08DjPrHSf1dqSd3bwJ_D1Lb5wJ4Qt_CesAYaCwoJBEDhWZORAnVYKkYwRAIgFNzhnftocLlVHJl1onuhbSUM7MysXPV6dawHAA0DZNsCIDRVu9gnPTH5UkcRXLtt7MLHCo4-DL-RCMyTcMxYBXL0`,
|
||||||
const executable = config.codexExe;
|
`--bootstrap-node=spr:CiUIAhIhAzZn3JmJab46BNjadVnLNQKbhnN3eYxwqpteKYY32SbOEgIDARo8CicAJQgCEiEDNmfcmYlpvjoE2Np1Wcs1ApuGc3d5jHCqm14phjfZJs4QrvWesAYaCwoJBKpA-TaRAnViKkcwRQIhANuMmZDD2c25xzTbKSirEpkZYoxbq-FU_lpI0K0e4mIVAiBfQX4yR47h1LCnHznXgDs6xx5DLO5q3lUcicqUeaqGeg`,
|
||||||
const args = [
|
`--bootstrap-node=spr:CiUIAhIhAgybmRwboqDdUJjeZrzh43sn5mp8jt6ENIb08tLn4x01EgIDARo8CicAJQgCEiECDJuZHBuioN1QmN5mvOHjeyfmanyO3oQ0hvTy0ufjHTUQh4ifsAYaCwoJBI_0zSiRAnVsKkcwRQIhAJCb_z0E3RsnQrEePdJzMSQrmn_ooHv6mbw1DOh5IbVNAiBbBJrWR8eBV6ftzMd6ofa5khNA2h88OBhMqHCIzSjCeA`,
|
||||||
`--data-dir="${config.dataDir}"`,
|
`--bootstrap-node=spr:CiUIAhIhAntGLadpfuBCD9XXfiN_43-V3L5VWgFCXxg4a8uhDdnYEgIDARo8CicAJQgCEiECe0Ytp2l-4EIP1dd-I3_jf5XcvlVaAUJfGDhry6EN2dgQsIufsAYaCwoJBNEmoCiRAnV2KkYwRAIgXO3bzd5VF8jLZG8r7dcLJ_FnQBYp1BcxrOvovEa40acCIDhQ14eJRoPwJ6GKgqOkXdaFAsoszl-HIRzYcXKeb7D9`,
|
||||||
`--log-level=DEBUG`,
|
`--log-level=DEBUG`,
|
||||||
`--log-file="${logFilePath}"`,
|
`--log-file="${logFilePath}"`,
|
||||||
`--storage-quota="${config.storageQuota}"`,
|
`--storage-quota="${config.storageQuota}"`,
|
||||||
`--disc-port=${config.ports.discPort}`,
|
`--disc-port=${config.ports.discPort}`,
|
||||||
`--listen-addrs=/ip4/0.0.0.0/tcp/${config.ports.listenPort}`,
|
`--listen-addrs=/ip4/0.0.0.0/tcp/${config.ports.listenPort}`,
|
||||||
`--api-port=${config.ports.apiPort}`,
|
`--api-port=${config.ports.apiPort}`,
|
||||||
`--nat=${nat}`,
|
`--nat=extip:${nat}`,
|
||||||
`--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`,
|
];
|
||||||
];
|
|
||||||
|
const command =
|
||||||
const command = `"${executable}" ${args.join(" ")}`;
|
`"${executable}" ${args.join(" ")}`
|
||||||
|
|
||||||
console.log(
|
console.log(showInfoMessage(
|
||||||
showInfoMessage(
|
'🚀 Codex node is running...\n\n' +
|
||||||
"🚀 Codex node is running...\n\n" +
|
'If your firewall ask, be sure to allow Codex to receive connections. \n' +
|
||||||
"If your firewall ask, be sure to allow Codex to receive connections. \n" +
|
'Please keep this terminal open. Start a new terminal to interact with the node.\n\n' +
|
||||||
"Please keep this terminal open. Start a new terminal to interact with the node.\n\n" +
|
'Press CTRL+C to stop the node'
|
||||||
"Press CTRL+C to stop the node",
|
));
|
||||||
),
|
|
||||||
);
|
const nodeProcess = exec(command);
|
||||||
|
|
||||||
const nodeProcess = exec(command);
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
try {
|
||||||
|
const response = await axios.get(`http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`);
|
||||||
try {
|
if (response.status === 200) {
|
||||||
const response = await axios.get(
|
// Check if wallet exists
|
||||||
`http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`,
|
try {
|
||||||
);
|
const existingWallet = await getWalletAddress();
|
||||||
if (response.status === 200) {
|
if (!existingWallet) {
|
||||||
// Check if wallet exists
|
console.log(showInfoMessage('[OPTIONAL] Please provide your ERC20 wallet address.'));
|
||||||
try {
|
const wallet = await promptForWalletAddress();
|
||||||
const existingWallet = await getWalletAddress();
|
if (wallet) {
|
||||||
if (!existingWallet) {
|
await setWalletAddress(wallet);
|
||||||
console.log(
|
console.log(showSuccessMessage('Wallet address saved successfully!'));
|
||||||
showInfoMessage(
|
}
|
||||||
"[OPTIONAL] Please provide your ERC20 wallet address.",
|
}
|
||||||
),
|
} catch (error) {
|
||||||
);
|
console.log(showErrorMessage('Failed to process wallet address. Continuing without wallet update.'));
|
||||||
const wallet = await promptForWalletAddress();
|
}
|
||||||
if (wallet) {
|
|
||||||
await setWalletAddress(wallet);
|
// Start periodic logging
|
||||||
console.log(
|
const stopLogging = await startPeriodicLogging(config);
|
||||||
showSuccessMessage("Wallet address saved successfully!"),
|
|
||||||
);
|
nodeProcess.on('exit', () => {
|
||||||
}
|
stopLogging();
|
||||||
}
|
});
|
||||||
} catch (error) {
|
|
||||||
console.log(
|
console.log(boxen(
|
||||||
showErrorMessage(
|
chalk.cyan('We are logging some of your node\'s public data for improving the Codex experience'),
|
||||||
"Failed to process wallet address. Continuing without wallet update.",
|
{
|
||||||
),
|
padding: 1,
|
||||||
);
|
margin: 1,
|
||||||
}
|
borderStyle: 'round',
|
||||||
|
borderColor: 'cyan',
|
||||||
// Start periodic logging
|
title: '🔒 Privacy Notice',
|
||||||
const stopLogging = await startPeriodicLogging(config);
|
titleAlignment: 'center',
|
||||||
|
dimBorder: true
|
||||||
nodeProcess.on("exit", () => {
|
}
|
||||||
stopLogging();
|
));
|
||||||
});
|
}
|
||||||
|
} catch (error) {
|
||||||
console.log(
|
// Silently handle any logging errors
|
||||||
boxen(
|
}
|
||||||
chalk.cyan(
|
|
||||||
"We are logging some of your node's public data for improving the Codex experience",
|
await new Promise((resolve, reject) => {
|
||||||
),
|
nodeProcess.on('exit', (code) => {
|
||||||
{
|
if (code === 0) resolve();
|
||||||
padding: 1,
|
else reject(new Error(`Node exited with code ${code}`));
|
||||||
margin: 1,
|
});
|
||||||
borderStyle: "round",
|
});
|
||||||
borderColor: "cyan",
|
|
||||||
title: "🔒 Privacy Notice",
|
if (platform === 'win32') {
|
||||||
titleAlignment: "center",
|
console.log(showInfoMessage('Cleaning up firewall rules...'));
|
||||||
dimBorder: true,
|
await runCommand('netsh advfirewall firewall delete rule name="Allow Codex (TCP-In)"');
|
||||||
},
|
await runCommand('netsh advfirewall firewall delete rule name="Allow Codex (UDP-In)"');
|
||||||
),
|
}
|
||||||
);
|
|
||||||
}
|
} catch (error) {
|
||||||
} catch (error) {
|
console.log(showErrorMessage(`Failed to run Codex: ${error.message}`));
|
||||||
// Silently handle any logging errors
|
}
|
||||||
}
|
await showNavigationMenu();
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
nodeProcess.on("exit", (code) => {
|
|
||||||
if (code === 0) resolve();
|
|
||||||
else reject(new Error(`Node exited with code ${code}`));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (platform === "win32") {
|
|
||||||
console.log(showInfoMessage("Cleaning up firewall rules..."));
|
|
||||||
await runCommand(
|
|
||||||
'netsh advfirewall firewall delete rule name="Allow Codex (TCP-In)"',
|
|
||||||
);
|
|
||||||
await runCommand(
|
|
||||||
'netsh advfirewall firewall delete rule name="Allow Codex (UDP-In)"',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(showErrorMessage(`Failed to run Codex: ${error.message}`));
|
|
||||||
}
|
}
|
||||||
await showNavigationMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showNodeDetails(data, showNavigationMenu) {
|
async function showNodeDetails(data, showNavigationMenu) {
|
||||||
const { choice } = await inquirer.prompt([
|
const { choice } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: "list",
|
type: 'list',
|
||||||
name: "choice",
|
name: 'choice',
|
||||||
message: "Select information to view:",
|
message: 'Select information to view:',
|
||||||
choices: [
|
choices: [
|
||||||
"1. View Connected Peers",
|
'1. View Connected Peers',
|
||||||
"2. View Node Information",
|
'2. View Node Information',
|
||||||
"3. Update Wallet Address",
|
'3. Update Wallet Address',
|
||||||
"4. Back to Main Menu",
|
'4. Back to Main Menu',
|
||||||
"5. Exit",
|
'5. Exit'
|
||||||
],
|
],
|
||||||
pageSize: 5,
|
pageSize: 5,
|
||||||
loop: true,
|
loop: true
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
switch (choice.split(".")[0].trim()) {
|
|
||||||
case "1":
|
|
||||||
const peerCount = data.table.nodes.length;
|
|
||||||
if (peerCount > 0) {
|
|
||||||
console.log(showInfoMessage("Connected Peers"));
|
|
||||||
data.table.nodes.forEach((node, index) => {
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
`Peer ${index + 1}:\n` +
|
|
||||||
`${chalk.cyan("Peer ID:")} ${node.peerId}\n` +
|
|
||||||
`${chalk.cyan("Address:")} ${node.address}\n` +
|
|
||||||
`${chalk.cyan("Status:")} ${node.seen ? chalk.green("Active") : chalk.gray("Inactive")}`,
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
margin: 1,
|
|
||||||
borderStyle: "round",
|
|
||||||
borderColor: "blue",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.log(showInfoMessage("No connected peers found."));
|
|
||||||
}
|
|
||||||
return showNodeDetails(data, showNavigationMenu);
|
|
||||||
case "2":
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
`${chalk.cyan("Version:")} ${data.codex.version}\n` +
|
|
||||||
`${chalk.cyan("Revision:")} ${data.codex.revision}\n\n` +
|
|
||||||
`${chalk.cyan("Node ID:")} ${data.table.localNode.nodeId}\n` +
|
|
||||||
`${chalk.cyan("Peer ID:")} ${data.table.localNode.peerId}\n` +
|
|
||||||
`${chalk.cyan("Listening Address:")} ${data.table.localNode.address}\n\n` +
|
|
||||||
`${chalk.cyan("Public IP:")} ${data.announceAddresses[0].split("/")[2]}\n` +
|
|
||||||
`${chalk.cyan("Port:")} ${data.announceAddresses[0].split("/")[4]}\n` +
|
|
||||||
`${chalk.cyan("Connected Peers:")} ${data.table.nodes.length}`,
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
margin: 1,
|
|
||||||
borderStyle: "round",
|
|
||||||
borderColor: "yellow",
|
|
||||||
title: "📊 Node Information",
|
|
||||||
titleAlignment: "center",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return showNodeDetails(data, showNavigationMenu);
|
|
||||||
case "3":
|
|
||||||
try {
|
|
||||||
const existingWallet = await getWalletAddress();
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
boxen(
|
|
||||||
`${chalk.cyan("Current wallet address:")}\n${existingWallet || "Not set"}`,
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
margin: 1,
|
|
||||||
borderStyle: "round",
|
|
||||||
borderColor: "blue",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const wallet = await promptForWalletAddress();
|
|
||||||
if (wallet) {
|
|
||||||
await setWalletAddress(wallet);
|
|
||||||
console.log(
|
|
||||||
showSuccessMessage("Wallet address updated successfully!"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
]);
|
||||||
console.log(
|
|
||||||
showErrorMessage(`Failed to update wallet address: ${error.message}`),
|
switch (choice.split('.')[0].trim()) {
|
||||||
);
|
case '1':
|
||||||
}
|
const peerCount = data.table.nodes.length;
|
||||||
return showNodeDetails(data, showNavigationMenu);
|
if (peerCount > 0) {
|
||||||
case "4":
|
console.log(showInfoMessage('Connected Peers'));
|
||||||
return showNavigationMenu();
|
data.table.nodes.forEach((node, index) => {
|
||||||
case "5":
|
console.log(boxen(
|
||||||
process.exit(0);
|
`Peer ${index + 1}:\n` +
|
||||||
}
|
`${chalk.cyan('Peer ID:')} ${node.peerId}\n` +
|
||||||
|
`${chalk.cyan('Address:')} ${node.address}\n` +
|
||||||
|
`${chalk.cyan('Status:')} ${node.seen ? chalk.green('Active') : chalk.gray('Inactive')}`,
|
||||||
|
{
|
||||||
|
padding: 1,
|
||||||
|
margin: 1,
|
||||||
|
borderStyle: 'round',
|
||||||
|
borderColor: 'blue'
|
||||||
|
}
|
||||||
|
));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(showInfoMessage('No connected peers found.'));
|
||||||
|
}
|
||||||
|
return showNodeDetails(data, showNavigationMenu);
|
||||||
|
case '2':
|
||||||
|
console.log(boxen(
|
||||||
|
`${chalk.cyan('Version:')} ${data.codex.version}\n` +
|
||||||
|
`${chalk.cyan('Revision:')} ${data.codex.revision}\n\n` +
|
||||||
|
`${chalk.cyan('Node ID:')} ${data.table.localNode.nodeId}\n` +
|
||||||
|
`${chalk.cyan('Peer ID:')} ${data.table.localNode.peerId}\n` +
|
||||||
|
`${chalk.cyan('Listening Address:')} ${data.table.localNode.address}\n\n` +
|
||||||
|
`${chalk.cyan('Public IP:')} ${data.announceAddresses[0].split('/')[2]}\n` +
|
||||||
|
`${chalk.cyan('Port:')} ${data.announceAddresses[0].split('/')[4]}\n` +
|
||||||
|
`${chalk.cyan('Connected Peers:')} ${data.table.nodes.length}`,
|
||||||
|
{
|
||||||
|
padding: 1,
|
||||||
|
margin: 1,
|
||||||
|
borderStyle: 'round',
|
||||||
|
borderColor: 'yellow',
|
||||||
|
title: '📊 Node Information',
|
||||||
|
titleAlignment: 'center'
|
||||||
|
}
|
||||||
|
));
|
||||||
|
return showNodeDetails(data, showNavigationMenu);
|
||||||
|
case '3':
|
||||||
|
try {
|
||||||
|
const existingWallet = await getWalletAddress();
|
||||||
|
|
||||||
|
console.log(boxen(
|
||||||
|
`${chalk.cyan('Current wallet address:')}\n${existingWallet || 'Not set'}`,
|
||||||
|
{
|
||||||
|
padding: 1,
|
||||||
|
margin: 1,
|
||||||
|
borderStyle: 'round',
|
||||||
|
borderColor: 'blue'
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
const wallet = await promptForWalletAddress();
|
||||||
|
if (wallet) {
|
||||||
|
await setWalletAddress(wallet);
|
||||||
|
console.log(showSuccessMessage('Wallet address updated successfully!'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(showErrorMessage(`Failed to update wallet address: ${error.message}`));
|
||||||
|
}
|
||||||
|
return showNodeDetails(data, showNavigationMenu);
|
||||||
|
case '4':
|
||||||
|
return showNavigationMenu();
|
||||||
|
case '5':
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkNodeStatus(config, showNavigationMenu) {
|
export async function checkNodeStatus(config, showNavigationMenu) {
|
||||||
try {
|
try {
|
||||||
const nodeRunning = await isNodeRunning(config);
|
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(
|
const response = await runCommand(`curl http://localhost:${config.ports.apiPort}/api/codex/v1/debug/info`);
|
||||||
`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);
|
||||||
|
|
||||||
const peerCount = data.table.nodes.length;
|
const peerCount = data.table.nodes.length;
|
||||||
const isOnline = peerCount > 2;
|
const isOnline = peerCount > 2;
|
||||||
|
|
||||||
console.log(
|
console.log(boxen(
|
||||||
boxen(
|
isOnline
|
||||||
isOnline
|
? chalk.green('Node is ONLINE & DISCOVERABLE')
|
||||||
? chalk.green("Node is ONLINE & DISCOVERABLE")
|
: chalk.yellow('Node is ONLINE but has few peers'),
|
||||||
: chalk.yellow("Node is ONLINE but has few peers"),
|
{
|
||||||
{
|
padding: 1,
|
||||||
padding: 1,
|
margin: 1,
|
||||||
margin: 1,
|
borderStyle: 'round',
|
||||||
borderStyle: "round",
|
borderColor: isOnline ? 'green' : 'yellow',
|
||||||
borderColor: isOnline ? "green" : "yellow",
|
title: '🔌 Node Status',
|
||||||
title: "🔌 Node Status",
|
titleAlignment: 'center'
|
||||||
titleAlignment: "center",
|
}
|
||||||
},
|
));
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await showNodeDetails(data, showNavigationMenu);
|
await showNodeDetails(data, showNavigationMenu);
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log(showErrorMessage('Codex node is not running. Try again after starting the node'));
|
||||||
showErrorMessage(
|
await showNavigationMenu();
|
||||||
"Codex node is not running. Try again after starting the node",
|
}
|
||||||
),
|
} catch (error) {
|
||||||
);
|
console.log(showErrorMessage(`Failed to check node status: ${error.message}`));
|
||||||
await showNavigationMenu();
|
await showNavigationMenu();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}
|
||||||
console.log(
|
|
||||||
showErrorMessage(`Failed to check node status: ${error.message}`),
|
|
||||||
);
|
|
||||||
await showNavigationMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user