diff --git a/src/main.js b/src/main.js index b2162fa..d2e449d 100644 --- a/src/main.js +++ b/src/main.js @@ -39,6 +39,7 @@ import { MarketplaceSetup } from "./ui/marketplaceSetup.js"; import { DataService } from "./services/dataService.js"; import { DataMenu } from "./ui/dataMenu.js"; import { FeedbackService } from "./services/feedbackService.js"; +import { NodeStatusMenu } from "./ui/nodeStatusMenu.js"; async function showNavigationMenu() { console.log("\n"); @@ -153,6 +154,7 @@ export async function main() { const dataService = new DataService(configService); const dataMenu = new DataMenu(uiService, fsService, dataService); const feedbackService = new FeedbackService(); + const nodeStatusMenu = new NodeStatusMenu(uiService, dataService, new MenuLoop()); const mainMenu = new MainMenu( uiService, new MenuLoop(), @@ -163,6 +165,7 @@ export async function main() { codexApp, dataMenu, feedbackService, + nodeStatusMenu, ); await mainMenu.show(); diff --git a/src/services/dataService.js b/src/services/dataService.js index d77ab40..84cd137 100644 --- a/src/services/dataService.js +++ b/src/services/dataService.js @@ -42,11 +42,26 @@ export class DataService { return filename; }; - getCodexData = () => { + debugInfo = async () => { + const debug = this.getCodexDebug(); + return await debug.info(); + } + + + + getCodex = () =>{ const config = this.configService.get(); const url = `http://localhost:${config.ports.apiPort}`; const codex = new Codex(url); - return codex.data; + return codex; + }; + + getCodexData = () => { + return this.getCodex().data; + }; + + getCodexDebug = () =>{ + return this.getCodex().debug; }; getFilename = (manifest) => { diff --git a/src/services/uiService.js b/src/services/uiService.js index 1ce5955..b22b59a 100644 --- a/src/services/uiService.js +++ b/src/services/uiService.js @@ -10,14 +10,14 @@ function show(msg) { } export class UiService { - showSuccessMessage = (message) => { + showSuccessMessage = (message, title = "✅ SUCCESS") => { show( boxen(chalk.green(message), { padding: 1, margin: 1, borderStyle: "round", borderColor: "green", - title: "✅ SUCCESS", + title: title, titleAlignment: "center", }), ); @@ -36,14 +36,14 @@ export class UiService { ); }; - showInfoMessage = (message) => { + showInfoMessage = (message, title = "ℹ️ INFO") => { show( boxen(chalk.cyan(message), { padding: 1, margin: 1, borderStyle: "round", borderColor: "cyan", - title: "ℹ️ INFO", + title: title, titleAlignment: "center", }), ); diff --git a/src/ui/dataMenu.js b/src/ui/dataMenu.js index f6c0fe9..ae0204b 100644 --- a/src/ui/dataMenu.js +++ b/src/ui/dataMenu.js @@ -34,10 +34,6 @@ export class DataMenu { } }; - showNodeStatus = async () => { - - }; - showLocalData = async () => { }; diff --git a/src/ui/mainMenu.js b/src/ui/mainMenu.js index 27c5c3c..e1ab8ae 100644 --- a/src/ui/mainMenu.js +++ b/src/ui/mainMenu.js @@ -9,6 +9,7 @@ export class MainMenu { codexApp, dataMenu, feedbackService, + nodeStatusMenu, ) { this.ui = uiService; this.loop = menuLoop; @@ -19,6 +20,7 @@ export class MainMenu { this.codexApp = codexApp; this.dataMenu = dataMenu; this.feedbackService = feedbackService; + this.nodeStatusMenu = nodeStatusMenu; this.loop.initialize(this.promptMainMenu); } @@ -41,7 +43,7 @@ export class MainMenu { }, { label: "Check node status", - action: this.dataMenu.showNodeStatus, + action: this.nodeStatusMenu.showNodeStatus, }, { label: "Upload a file", diff --git a/src/ui/nodeStatusMenu.js b/src/ui/nodeStatusMenu.js new file mode 100644 index 0000000..b19b75e --- /dev/null +++ b/src/ui/nodeStatusMenu.js @@ -0,0 +1,95 @@ +import chalk from "chalk"; + +export class NodeStatusMenu { + constructor(uiService, dataService, menuLoop) { + this.ui = uiService; + this.dataService = dataService; + this.loop = menuLoop; + + this.loop.initialize(this.showNodeStatusMenu); + } + + showNodeStatus = async () => { + this.debugInfo = await this.fetchDebugInfo(); + if (debugInfo == undefined) return; + + const peerCount = this.debugInfo.table.nodes.length; + const isOnline = peerCount > 2; + + if (isOnline) { + this.showSuccessMessage( + "Node is ONLINE & DISCOVERABLE", + "🔌 Node Status" + ) + } else { + this.showInfoMessage( + "Node is ONLINE but has few peers", + "🔌 Node Status" + ) + } + + await this.loop.showLoop(); + }; + + showNodeStatusMenu = async () => { + await this.ui.askMultipleChoice("Select information to view:", [ + { + label: "View connected peers", + action: this.showPeers, + }, + { + label: "View node information", + action: this.showNodeInfo, + }, + { + label: "Back to Main Menu", + action: this.loop.stopLoop, + }, + ]); + }; + + showPeers = async () => { + const peerCount = this.debugInfo.table.nodes.length; + if (peerCount > 0) { + this.ui.showInfoMessage('Connected Peers'); + this.debugInfo.table.nodes.forEach((node, index) => { + this.ui.showInfoMessage( + `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')}`, + ); + }); + } else { + this.ui.showInfoMessage('No connected peers found.'); + } + }; + + showNodeInfo = async () => { + const data = this.debugInfo; + this.ui.showInfoMessage( + `${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}`, + ); + }; + + fetchDebugInfo = async () => { + const spinner = this.ui.createAndStartSpinner("Fetching..."); + try { + return await this.dataService.debugInfo(); + } + catch { + this.ui.showErrorMessage("Failed to fetch debug/info"); + return; + } + finally { + this.ui.stopSpinnerSuccess(spinner); + } + } +}