import { execSync } from 'child_process'; import { Nupst } from './nupst.js'; import { logger } from './logger.js'; /** * Class for handling CLI commands * Provides interface between user commands and the application */ export class NupstCli { private readonly nupst: Nupst; /** * Create a new CLI handler */ constructor() { this.nupst = new Nupst(); } /** * Parse command line arguments and execute the appropriate command * @param args Command line arguments (process.argv) */ public async parseAndExecute(args: string[]): Promise { // Extract debug flag from any position const debugOptions = this.extractDebugOptions(args); if (debugOptions.debugMode) { logger.log('Debug mode enabled'); // Enable debug mode in the SNMP client this.nupst.getSnmp().enableDebug(); } // Get the command (default to help if none provided) const command = debugOptions.cleanedArgs[2] || 'help'; const commandArgs = debugOptions.cleanedArgs.slice(3); // Route to the appropriate command handler await this.executeCommand(command, commandArgs, debugOptions.debugMode); } /** * Extract and remove debug options from args * @param args Command line arguments * @returns Object with debug flags and cleaned args */ private extractDebugOptions(args: string[]): { debugMode: boolean; cleanedArgs: string[] } { const debugMode = args.includes('--debug') || args.includes('-d'); // Remove debug flags from args const cleanedArgs = args.filter((arg) => arg !== '--debug' && arg !== '-d'); return { debugMode, cleanedArgs }; } /** * Execute the command with the given arguments * @param command Command to execute * @param commandArgs Additional command arguments * @param debugMode Whether debug mode is enabled */ private async executeCommand(command: string, commandArgs: string[], debugMode: boolean): Promise { // Get access to the handlers const upsHandler = this.nupst.getUpsHandler(); const groupHandler = this.nupst.getGroupHandler(); const serviceHandler = this.nupst.getServiceHandler(); // Handle group subcommands if (command === 'group') { const subcommand = commandArgs[0] || 'list'; const subcommandArgs = commandArgs.slice(1); switch (subcommand) { case 'add': await groupHandler.add(); break; case 'edit': const groupId = subcommandArgs[0]; if (!groupId) { logger.error('Group ID is required for edit command'); this.showGroupHelp(); return; } await groupHandler.edit(groupId); break; case 'delete': const groupIdToDelete = subcommandArgs[0]; if (!groupIdToDelete) { logger.error('Group ID is required for delete command'); this.showGroupHelp(); return; } await groupHandler.delete(groupIdToDelete); break; case 'list': await groupHandler.list(); break; default: this.showGroupHelp(); break; } return; } // Handle main commands switch (command) { case 'add': await upsHandler.add(); break; case 'edit': const upsId = commandArgs[0]; await upsHandler.edit(upsId); break; case 'delete': const upsIdToDelete = commandArgs[0]; if (!upsIdToDelete) { logger.error('UPS ID is required for delete command'); this.showHelp(); return; } await upsHandler.delete(upsIdToDelete); break; case 'list': await upsHandler.list(); break; case 'setup': // Backward compatibility: setup is now an alias for edit with no specific UPS ID await upsHandler.edit(undefined); break; case 'enable': await serviceHandler.enable(); break; case 'daemon-start': await serviceHandler.daemonStart(debugMode); break; case 'logs': await serviceHandler.logs(); break; case 'stop': await serviceHandler.stop(); break; case 'start': await serviceHandler.start(); break; case 'status': await serviceHandler.status(); break; case 'disable': await serviceHandler.disable(); break; case 'test': await upsHandler.test(debugMode); break; case 'update': await serviceHandler.update(); break; case 'uninstall': await serviceHandler.uninstall(); break; case 'config': await this.showConfig(); break; case 'help': default: this.showHelp(); break; } } /** * Display the current configuration */ private async showConfig(): Promise { try { // Try to load configuration try { await this.nupst.getDaemon().loadConfig(); } catch (error) { const errorBoxWidth = 45; logger.logBoxTitle('Configuration Error', errorBoxWidth); logger.logBoxLine('No configuration found.'); logger.logBoxLine("Please run 'nupst setup' first to create a configuration."); logger.logBoxEnd(); return; } // Get current configuration const config = this.nupst.getDaemon().getConfig(); const boxWidth = 50; logger.logBoxTitle('NUPST Configuration', boxWidth); // Check if multi-UPS config if (config.upsDevices && Array.isArray(config.upsDevices)) { // Multi-UPS configuration logger.logBoxLine(`UPS Devices: ${config.upsDevices.length}`); logger.logBoxLine(`Groups: ${config.groups ? config.groups.length : 0}`); logger.logBoxLine(`Check Interval: ${config.checkInterval / 1000} seconds`); logger.logBoxLine(''); logger.logBoxLine('Configuration File Location:'); logger.logBoxLine(' /etc/nupst/config.json'); logger.logBoxEnd(); // Show UPS devices if (config.upsDevices.length > 0) { logger.logBoxTitle('UPS Devices', boxWidth); for (const ups of config.upsDevices) { logger.logBoxLine(`${ups.name} (${ups.id}):`); logger.logBoxLine(` Host: ${ups.snmp.host}:${ups.snmp.port}`); logger.logBoxLine(` Model: ${ups.snmp.upsModel}`); logger.logBoxLine(` Thresholds: ${ups.thresholds.battery}% battery, ${ups.thresholds.runtime} min runtime`); logger.logBoxLine(` Groups: ${ups.groups.length > 0 ? ups.groups.join(', ') : 'None'}`); logger.logBoxLine(''); } logger.logBoxEnd(); } // Show groups if (config.groups && config.groups.length > 0) { logger.logBoxTitle('UPS Groups', boxWidth); for (const group of config.groups) { logger.logBoxLine(`${group.name} (${group.id}):`); logger.logBoxLine(` Mode: ${group.mode}`); if (group.description) { logger.logBoxLine(` Description: ${group.description}`); } // List UPS devices in this group const upsInGroup = config.upsDevices.filter(ups => ups.groups && ups.groups.includes(group.id)); logger.logBoxLine(` UPS Devices: ${upsInGroup.length > 0 ? upsInGroup.map(ups => ups.name).join(', ') : 'None'}`); logger.logBoxLine(''); } logger.logBoxEnd(); } } else { // Legacy single UPS configuration // SNMP Settings logger.logBoxLine('SNMP Settings:'); logger.logBoxLine(` Host: ${config.snmp.host}`); logger.logBoxLine(` Port: ${config.snmp.port}`); logger.logBoxLine(` Version: ${config.snmp.version}`); logger.logBoxLine(` UPS Model: ${config.snmp.upsModel || 'cyberpower'}`); if (config.snmp.version === 1 || config.snmp.version === 2) { logger.logBoxLine(` Community: ${config.snmp.community}`); } else if (config.snmp.version === 3) { logger.logBoxLine(` Security Level: ${config.snmp.securityLevel}`); logger.logBoxLine(` Username: ${config.snmp.username}`); // Show auth and privacy details based on security level if ( config.snmp.securityLevel === 'authNoPriv' || config.snmp.securityLevel === 'authPriv' ) { logger.logBoxLine(` Auth Protocol: ${config.snmp.authProtocol || 'None'}`); } if (config.snmp.securityLevel === 'authPriv') { logger.logBoxLine(` Privacy Protocol: ${config.snmp.privProtocol || 'None'}`); } // Show timeout value logger.logBoxLine(` Timeout: ${config.snmp.timeout / 1000} seconds`); } // Show OIDs if custom model is selected if (config.snmp.upsModel === 'custom' && config.snmp.customOIDs) { logger.logBoxLine('Custom OIDs:'); logger.logBoxLine(` Power Status: ${config.snmp.customOIDs.POWER_STATUS || 'Not set'}`); logger.logBoxLine( ` Battery Capacity: ${config.snmp.customOIDs.BATTERY_CAPACITY || 'Not set'}` ); logger.logBoxLine(` Battery Runtime: ${config.snmp.customOIDs.BATTERY_RUNTIME || 'Not set'}`); } // Thresholds logger.logBoxLine('Thresholds:'); logger.logBoxLine(` Battery: ${config.thresholds.battery}%`); logger.logBoxLine(` Runtime: ${config.thresholds.runtime} minutes`); logger.logBoxLine(`Check Interval: ${config.checkInterval / 1000} seconds`); // Configuration file location logger.logBoxLine(''); logger.logBoxLine('Configuration File Location:'); logger.logBoxLine(' /etc/nupst/config.json'); logger.logBoxLine(''); logger.logBoxLine('Note: Using legacy single-UPS configuration format.'); logger.logBoxLine('Consider using "nupst add" to migrate to multi-UPS format.'); logger.logBoxEnd(); } // Show service status try { const isActive = execSync('systemctl is-active nupst.service || true').toString().trim() === 'active'; const isEnabled = execSync('systemctl is-enabled nupst.service || true').toString().trim() === 'enabled'; const statusBoxWidth = 45; logger.logBoxTitle('Service Status', statusBoxWidth); logger.logBoxLine(`Service Active: ${isActive ? 'Yes' : 'No'}`); logger.logBoxLine(`Service Enabled: ${isEnabled ? 'Yes' : 'No'}`); logger.logBoxEnd(); } catch (error) { // Ignore errors checking service status } } catch (error) { logger.error(`Failed to display configuration: ${error.message}`); } } /** * Display help message */ private showHelp(): void { logger.log(` NUPST - Node.js UPS Shutdown Tool Usage: nupst enable - Install and enable the systemd service (requires root) nupst disable - Stop and uninstall the systemd service (requires root) nupst daemon-start - Start the daemon process directly nupst logs - Show logs of the systemd service nupst stop - Stop the systemd service nupst start - Start the systemd service nupst status - Show status of the systemd service and UPS status UPS Management: nupst add - Add a new UPS device nupst edit [id] - Edit an existing UPS (default UPS if no ID provided) nupst delete - Delete a UPS by ID nupst list - List all configured UPS devices nupst setup - Alias for 'nupst edit' (backward compatibility) Group Management: nupst group list - List all UPS groups nupst group add - Add a new UPS group nupst group edit - Edit an existing UPS group nupst group delete - Delete a UPS group System Commands: nupst test - Test the current configuration by connecting to all UPS devices nupst config - Display the current configuration nupst update - Update NUPST from repository and refresh systemd service (requires root) nupst uninstall - Completely uninstall NUPST from the system (requires root) nupst help - Show this help message Options: --debug, -d - Enable debug mode for detailed SNMP logging (Example: nupst test --debug) `); } /** * Display help message for group commands */ private showGroupHelp(): void { logger.log(` NUPST - Group Management Commands Usage: nupst group list - List all UPS groups nupst group add - Add a new UPS group nupst group edit - Edit an existing UPS group nupst group delete - Delete a UPS group Options: --debug, -d - Enable debug mode for detailed logging `); } }