#!/usr/bin/env node import { RingNode } from './src/ring-node.js'; import chalk from 'chalk'; import { readFileSync } from 'fs'; const args = process.argv.slice(2); const options = {}; // Parse command line arguments for (let i = 0; i < args.length; i++) { switch (args[i]) { case '--port': options.port = parseInt(args[++i]); break; case '--id': options.id = args[++i]; break; case '--bootstrap': options.bootstrap = args[++i]; break; case '--ice-servers': try { options.iceServers = JSON.parse(args[++i]); } catch (error) { console.error(chalk.red('Error parsing ICE servers JSON:'), error.message); process.exit(1); } break; case '--config': try { const configPath = args[++i]; const configData = JSON.parse(readFileSync(configPath, 'utf8')); if (configData.iceServers) { options.iceServers = configData.iceServers; } } catch (error) { console.error(chalk.red('Error loading config file:'), error.message); process.exit(1); } break; case '--help': console.log(` ${chalk.blue('Ring Network Node')} Usage: node node.js [options] Options: --port Port to listen on (default: random) --id Node ID (default: auto-generated) --bootstrap Bootstrap node address (host:port) --ice-servers ICE servers configuration (JSON array) --config Load configuration from JSON file --help Show this help message Examples: node node.js --port 8080 node node.js --port 8081 --bootstrap localhost:8080 node node.js --id mynode --port 8082 --bootstrap localhost:8080 node node.js --config config/ice-servers-with-turn.json --port 8080 Note: Ring positions are now assigned automatically for optimal network topology. ICE Servers Example: --ice-servers '[{"urls":"stun:stun.l.google.com:19302"},{"urls":"turn:turn.example.com:3478","username":"user","credential":"pass"}]' Config File Example: config/ice-servers-default.json - Default STUN servers config/ice-servers-with-turn.json - With TURN server config/ice-servers-public-turn.json - Public TURN servers `); process.exit(0); break; } } const node = new RingNode(options); // Handle graceful shutdown process.on('SIGINT', async () => { console.log(chalk.yellow('\nšŸ›‘ Received shutdown signal...')); await node.destroy(); process.exit(0); }); process.on('SIGTERM', async () => { console.log(chalk.yellow('\nšŸ›‘ Received termination signal...')); await node.destroy(); process.exit(0); }); // Connect to bootstrap node if specified if (options.bootstrap) { setTimeout(async () => { try { await node.joinRing(options.bootstrap); } catch (error) { // Connection failed } }, 2000); } // Set up event handlers node.on('peerConnected', (peerId) => { // Peer connected }); node.on('peerDisconnected', (peerId) => { // Peer disconnected }); node.on('ringMessage', ({ from, payload }) => { // Ring message received }); node.on('networkUpdate', ({ from, payload }) => { // Network update received }); // Interactive commands process.stdin.setEncoding('utf8'); process.stdin.on('readable', () => { const chunk = process.stdin.read(); if (chunk !== null) { const command = chunk.trim(); handleCommand(command); } }); function handleCommand(command) { const [cmd, ...args] = command.split(' '); switch (cmd) { case 'send': if (args.length > 0) { const message = args.join(' '); node.sendRingMessage({ type: 'chat', content: message }); } break; case 'info': const info = node.getNetworkInfo(); console.log(chalk.blue('\nšŸ“Š Network Info:')); console.log(JSON.stringify(info, null, 2)); break; case 'peers': const peers = node.webrtc.getConnectedPeers(); console.log(chalk.blue(`\nšŸ‘„ Connected Peers (${peers.length}):`)); peers.forEach(peer => { console.log(` - ${peer.substring(0, 8)}...`); }); break; case 'connections': const status = node.getConnectionStatus(); const persistentConns = node.getPersistentConnections(); console.log(chalk.blue('\nšŸ”— Connection Status:')); console.log(` Total Connections: ${status.totalConnections}`); console.log(` Persistent Connections: ${status.persistentConnections}`); console.log(` Active Connections: ${status.activeConnections}`); console.log(` Known Nodes: ${status.knownNodes}`); console.log(` Oracle Nodes: ${status.oracleNodes}`); if (persistentConns.length > 0) { console.log(chalk.blue('\nšŸ”„ Persistent Connections:')); persistentConns.forEach(conn => { const timeSince = conn.lastSeen ? `${Math.floor((Date.now() - conn.lastSeen) / 1000)}s ago` : 'never'; const statusColor = conn.connectionState === 'connected' ? chalk.green : chalk.red; console.log(` - ${conn.nodeId.substring(0, 8)}... [${statusColor(conn.connectionState)}] ${conn.isOracle ? 'šŸ”®' : 'šŸ’¾'} (${timeSince})`); }); } break; case 'topology': const topology = node.getRingTopology(); console.log(chalk.blue('\nšŸ”„ Ring Topology:')); console.log(` Ring Size: ${topology.ringSize}`); console.log(` Total Nodes: ${topology.totalNodes}`); console.log(` My Position: ${node.ringPosition}`); if (topology.nodes.length > 0) { console.log(chalk.yellow('\nšŸ“ Node Positions:')); topology.nodes.forEach(nodeInfo => { const indicators = []; if (nodeInfo.isThisNode) indicators.push(chalk.green('ME')); if (nodeInfo.isOracle) indicators.push(chalk.yellow('šŸ”®')); if (nodeInfo.isConnected) indicators.push(chalk.green('āœ“')); const indicatorStr = indicators.length > 0 ? ` [${indicators.join(' ')}]` : ''; console.log(` ${nodeInfo.position.toString().padStart(4)}: ${nodeInfo.nodeId}...${indicatorStr}`); }); console.log(chalk.cyan('\nšŸ”„ Ring Connections:')); console.log(` Inner Ring: ${topology.innerRing.left}... ← ME → ${topology.innerRing.right}...`); console.log(` Outer Ring: ${topology.outerRing.left}... ← ME → ${topology.outerRing.right}...`); } break; case 'help': console.log(chalk.blue(` šŸ“š Available Commands: send - Send a message through the ring info - Show network information peers - List connected peers connections - Show persistent connection status topology - Show ring topology and positions help - Show this help quit - Exit the node `)); break; case 'quit': case 'exit': console.log(chalk.yellow('šŸ‘‹ Goodbye!')); process.exit(0); break; default: if (command.length > 0) { console.log(chalk.red(`ā“ Unknown command: ${cmd}. Type 'help' for available commands.`)); } } } console.log(chalk.blue(` šŸ”— Ring Network Node Started! Node ID: ${node.id.substring(0, 8)}... Port: ${node.port} Type 'help' for available commands. `));