From 60a9576fbe731c6a96fef8192b30c481f08f0dcc Mon Sep 17 00:00:00 2001 From: Kumaraguru <19eucs071@skcet.ac.in> Date: Mon, 13 Jan 2025 19:20:52 +0000 Subject: [PATCH] getting erc20 addresses optionally after running node --- package.json | 2 +- src/handlers/nodeHandlers.js | 73 +++++++++++++++++-- src/services/nodeService.js | 132 ++++++++++++++++++++++++++++------- 3 files changed, 174 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index bb17f45..3945bff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codexstorage", - "version": "1.0.7", + "version": "1.0.8", "description": "CLI tool for Codex Storage", "main": "index.js", "type": "module", diff --git a/src/handlers/nodeHandlers.js b/src/handlers/nodeHandlers.js index c5bea59..295e2a3 100644 --- a/src/handlers/nodeHandlers.js +++ b/src/handlers/nodeHandlers.js @@ -1,7 +1,7 @@ import { createSpinner } from 'nanospinner'; import { runCommand } from '../utils/command.js'; import { showErrorMessage, showInfoMessage, showSuccessMessage } from '../utils/messages.js'; -import { isNodeRunning, isCodexInstalled, logToSupabase } from '../services/nodeService.js'; +import { isNodeRunning, isCodexInstalled, logToSupabase, startPeriodicLogging, getWalletAddress, setWalletAddress } from '../services/nodeService.js'; import inquirer from 'inquirer'; import boxen from 'boxen'; import chalk from 'chalk'; @@ -11,6 +11,22 @@ import axios from 'axios'; const platform = os.platform(); +async function promptForWalletAddress() { + const { wallet } = await inquirer.prompt([ + { + type: 'input', + name: 'wallet', + message: 'Please enter your ERC20 wallet address (or press enter to skip):', + validate: (input) => { + if (!input) return true; // Allow empty input + 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 wallet || null; +} + export async function runCodex(showNavigationMenu) { const isInstalled = await isCodexInstalled(); if (!isInstalled) { @@ -84,7 +100,28 @@ export async function runCodex(showNavigationMenu) { try { const response = await axios.get('http://localhost:8080/api/codex/v1/debug/info'); if (response.status === 200) { - await logToSupabase(response.data); + // Check if wallet exists + try { + const existingWallet = await getWalletAddress(); + if (!existingWallet) { + console.log(showInfoMessage('[OPTIONAL] Please provide your ERC20 wallet address.')); + const wallet = await promptForWalletAddress(); + if (wallet) { + await setWalletAddress(wallet); + console.log(showSuccessMessage('Wallet address saved successfully!')); + } + } + } catch (error) { + console.log(showErrorMessage('Failed to process wallet address. Continuing without wallet update.')); + } + + // Start periodic logging + const stopLogging = await startPeriodicLogging(); + + nodeProcess.on('exit', () => { + stopLogging(); + }); + console.log(boxen( chalk.cyan('We are logging some of your node\'s public data for improving the Codex experience'), { @@ -131,10 +168,11 @@ async function showNodeDetails(data, showNavigationMenu) { choices: [ '1. View Connected Peers', '2. View Node Information', - '3. Back to Main Menu', - '4. Exit' + '3. Update Wallet Address', + '4. Back to Main Menu', + '5. Exit' ], - pageSize: 4, + pageSize: 5, loop: true } ]); @@ -183,8 +221,31 @@ async function showNodeDetails(data, showNavigationMenu) { )); return showNodeDetails(data, showNavigationMenu); case '3': - return showNavigationMenu(); + 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); } } diff --git a/src/services/nodeService.js b/src/services/nodeService.js index 1fecea0..c5845a6 100644 --- a/src/services/nodeService.js +++ b/src/services/nodeService.js @@ -5,6 +5,21 @@ import os from 'os'; const platform = os.platform(); +// Add a variable to store wallet address in memory +let currentWallet = null; + +export async function setWalletAddress(wallet) { + // Basic ERC20 address validation + if (wallet && !/^0x[a-fA-F0-9]{40}$/.test(wallet)) { + throw new Error('Invalid ERC20 wallet address format'); + } + currentWallet = wallet; +} + +export async function getWalletAddress() { + return currentWallet; +} + export async function isNodeRunning() { try { const response = await axios.get('http://localhost:8080/api/codex/v1/debug/info'); @@ -23,36 +38,53 @@ export async function isCodexInstalled() { } } -export async function logToSupabase(nodeData) { - try { - const peerCount = nodeData.table.nodes ? nodeData.table.nodes.length : "0"; - const payload = { - nodeId: nodeData.table.localNode.nodeId, - peerId: nodeData.table.localNode.peerId, - publicIp: nodeData.announceAddresses[0].split('/')[2], - version: nodeData.codex.version, - peerCount: peerCount == 0 ? "0" : peerCount, - port: nodeData.announceAddresses[0].split('/')[4], - listeningAddress: nodeData.table.localNode.address - }; +export async function logToSupabase(nodeData, retryCount = 3, retryDelay = 1000) { + const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); - const response = await axios.post('https://vfcnsjxahocmzefhckfz.supabase.co/functions/v1/codexnodes', payload, { - headers: { - 'Content-Type': 'application/json' - } - }); - - return response.status === 200; - } catch (error) { - console.error('Failed to log to Supabase:', error.message); - if (error.response) { - console.error('Error response:', { - status: error.response.status, - data: error.response.data + for (let attempt = 1; attempt <= retryCount; attempt++) { + try { + const peerCount = nodeData.table.nodes ? nodeData.table.nodes.length : "0"; + const payload = { + nodeId: nodeData.table.localNode.nodeId, + peerId: nodeData.table.localNode.peerId, + publicIp: nodeData.announceAddresses[0].split('/')[2], + version: nodeData.codex.version, + peerCount: peerCount == 0 ? "0" : peerCount, + port: nodeData.announceAddresses[0].split('/')[4], + listeningAddress: nodeData.table.localNode.address, + timestamp: new Date().toISOString(), + wallet: currentWallet // Include wallet address in payload + }; + + const response = await axios.post('https://vfcnsjxahocmzefhckfz.supabase.co/functions/v1/codexnodes', payload, { + headers: { + 'Content-Type': 'application/json' + }, + timeout: 5000 // 5 second timeout }); + + return response.status === 200; + } catch (error) { + const isLastAttempt = attempt === retryCount; + const isNetworkError = error.code === 'ENOTFOUND' || error.code === 'ETIMEDOUT' || error.code === 'ECONNREFUSED'; + + if (isLastAttempt || !isNetworkError) { + console.error(`Failed to log to Supabase (attempt ${attempt}/${retryCount}):`, error.message); + if (error.response) { + console.error('Error response:', { + status: error.response.status, + data: error.response.data + }); + } + if (isLastAttempt) return false; + } else { + // Only log retry attempts for network errors + console.log(`Retrying Supabase log (attempt ${attempt}/${retryCount}) after ${retryDelay}ms...`); + await delay(retryDelay); + } } - return false; } + return false; } export async function checkDependencies() { @@ -70,4 +102,52 @@ export async function checkDependencies() { } } return true; +} + +export async function startPeriodicLogging() { + const FIFTEEN_MINUTES = 15 * 60 * 1000; // 15 minutes in milliseconds + + const logNodeInfo = async () => { + try { + const response = await axios.get('http://localhost:8080/api/codex/v1/debug/info'); + if (response.status === 200) { + await logToSupabase(response.data); + } + } catch (error) { + // Silently handle any logging errors to not disrupt the node operation + console.error('Failed to log node info:', error.message); + } + }; + + // Initial log + await logNodeInfo(); + + // Set up periodic logging + const intervalId = setInterval(logNodeInfo, FIFTEEN_MINUTES); + + // Return cleanup function + return () => clearInterval(intervalId); +} + +export async function updateWalletAddress(nodeId, wallet) { + // Basic ERC20 address validation + if (!/^0x[a-fA-F0-9]{40}$/.test(wallet)) { + throw new Error('Invalid ERC20 wallet address format'); + } + + try { + const response = await axios.post('https://vfcnsjxahocmzefhckfz.supabase.co/functions/v1/wallet', { + nodeId, + wallet + }, { + headers: { + 'Content-Type': 'application/json' + }, + timeout: 5000 + }); + return response.status === 200; + } catch (error) { + console.error('Failed to update wallet address:', error.message); + throw error; + } } \ No newline at end of file