From 9efcc4b437514b6c0198e158ee094e5a2bc44f28 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Sat, 18 Oct 2025 12:36:35 +0000 Subject: [PATCH] Phase 3: Reorganize CLI with subcommand structure Major Changes: - Reorganized commands into logical groups (service, ups, group) - Added new subcommand structure: - nupst service - nupst ups - nupst group - nupst config [show] - Added --version/-v flag support - Added restart subcommand for service - Added command aliases (ls, rm) - Renamed delete() to remove() in handlers - Maintained backward compatibility with deprecation warnings - Updated all help messages to reflect new structure - Added showVersion(), showServiceHelp(), showUpsHelp() methods - Fixed readline imports to use node:readline Breaking Changes: - Old command format (e.g. 'nupst add') is deprecated - Users should migrate to new format (e.g. 'nupst ups add') - Backward compatibility maintained with warnings for now --- ts/cli.ts | 363 ++++++++++++++++++++++++++++++---------- ts/cli/group-handler.ts | 8 +- ts/cli/ups-handler.ts | 8 +- 3 files changed, 282 insertions(+), 97 deletions(-) diff --git a/ts/cli.ts b/ts/cli.ts index b8c481e..7ef1296 100644 --- a/ts/cli.ts +++ b/ts/cli.ts @@ -21,7 +21,7 @@ export class NupstCli { * @param args Command line arguments (process.argv) */ public async parseAndExecute(args: string[]): Promise { - // Extract debug flag from any position + // Extract debug and version flags from any position const debugOptions = this.extractDebugOptions(args); if (debugOptions.debugMode) { logger.log('Debug mode enabled'); @@ -29,6 +29,12 @@ export class NupstCli { this.nupst.getSnmp().enableDebug(); } + // Check for version flag + if (debugOptions.cleanedArgs.includes('--version') || debugOptions.cleanedArgs.includes('-v')) { + this.showVersion(); + return; + } + // Get the command (default to help if none provided) const command = debugOptions.cleanedArgs[2] || 'help'; const commandArgs = debugOptions.cleanedArgs.slice(3); @@ -61,17 +67,92 @@ export class NupstCli { const upsHandler = this.nupst.getUpsHandler(); const groupHandler = this.nupst.getGroupHandler(); const serviceHandler = this.nupst.getServiceHandler(); - + + // Handle service subcommands + if (command === 'service') { + const subcommand = commandArgs[0] || 'status'; + + switch (subcommand) { + case 'enable': + await serviceHandler.enable(); + break; + case 'disable': + await serviceHandler.disable(); + break; + case 'start': + await serviceHandler.start(); + break; + case 'stop': + await serviceHandler.stop(); + break; + case 'restart': + await serviceHandler.stop(); + await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s + await serviceHandler.start(); + break; + case 'status': + await serviceHandler.status(); + break; + case 'logs': + await serviceHandler.logs(); + break; + case 'start-daemon': + await serviceHandler.daemonStart(debugMode); + break; + default: + this.showServiceHelp(); + break; + } + return; + } + + // Handle UPS subcommands + if (command === 'ups') { + const subcommand = commandArgs[0] || 'list'; + const subcommandArgs = commandArgs.slice(1); + + switch (subcommand) { + case 'add': + await upsHandler.add(); + break; + case 'edit': + const upsId = subcommandArgs[0]; + await upsHandler.edit(upsId); + break; + case 'remove': + case 'rm': // Alias + case 'delete': // Backward compatibility + const upsIdToRemove = subcommandArgs[0]; + if (!upsIdToRemove) { + logger.error('UPS ID is required for remove command'); + this.showUpsHelp(); + return; + } + await upsHandler.remove(upsIdToRemove); + break; + case 'list': + case 'ls': // Alias + await upsHandler.list(); + break; + case 'test': + await upsHandler.test(debugMode); + break; + default: + this.showUpsHelp(); + break; + } + return; + } + // 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) { @@ -81,21 +162,21 @@ export class NupstCli { } await groupHandler.edit(groupId); break; - - case 'delete': - const groupIdToDelete = subcommandArgs[0]; - if (!groupIdToDelete) { - logger.error('Group ID is required for delete command'); + case 'remove': + case 'rm': // Alias + case 'delete': // Backward compatibility + const groupIdToRemove = subcommandArgs[0]; + if (!groupIdToRemove) { + logger.error('Group ID is required for remove command'); this.showGroupHelp(); return; } - await groupHandler.delete(groupIdToDelete); + await groupHandler.remove(groupIdToRemove); break; - case 'list': + case 'ls': // Alias await groupHandler.list(); break; - default: this.showGroupHelp(); break; @@ -103,82 +184,100 @@ export class NupstCli { return; } - // Handle main commands + // Handle config subcommand + if (command === 'config') { + const subcommand = commandArgs[0] || 'show'; + + switch (subcommand) { + case 'show': + case 'display': + await this.showConfig(); + break; + default: + await this.showConfig(); + break; + } + return; + } + + // Handle top-level commands and backward compatibility switch (command) { + // Backward compatibility - old UPS commands case 'add': + logger.log("Note: 'nupst add' is deprecated. Use 'nupst ups add' instead."); await upsHandler.add(); break; - case 'edit': - const upsId = commandArgs[0]; - await upsHandler.edit(upsId); + logger.log("Note: 'nupst edit' is deprecated. Use 'nupst ups edit' instead."); + await upsHandler.edit(commandArgs[0]); break; - case 'delete': - const upsIdToDelete = commandArgs[0]; - if (!upsIdToDelete) { + logger.log("Note: 'nupst delete' is deprecated. Use 'nupst ups remove' instead."); + if (!commandArgs[0]) { logger.error('UPS ID is required for delete command'); this.showHelp(); return; } - await upsHandler.delete(upsIdToDelete); + await upsHandler.remove(commandArgs[0]); break; - case 'list': + logger.log("Note: 'nupst list' is deprecated. Use 'nupst ups list' instead."); await upsHandler.list(); break; - - case 'setup': - // Backward compatibility: setup is now an alias for edit with no specific UPS ID + case 'test': + logger.log("Note: 'nupst test' is deprecated. Use 'nupst ups test' instead."); + await upsHandler.test(debugMode); + break; + case 'setup': + logger.log("Note: 'nupst setup' is deprecated. Use 'nupst ups edit' instead."); await upsHandler.edit(undefined); break; + // Backward compatibility - old service commands case 'enable': + logger.log("Note: 'nupst enable' is deprecated. Use 'nupst service enable' instead."); await serviceHandler.enable(); break; - + case 'disable': + logger.log("Note: 'nupst disable' is deprecated. Use 'nupst service disable' instead."); + await serviceHandler.disable(); + break; + case 'start': + logger.log("Note: 'nupst start' is deprecated. Use 'nupst service start' instead."); + await serviceHandler.start(); + break; + case 'stop': + logger.log("Note: 'nupst stop' is deprecated. Use 'nupst service stop' instead."); + await serviceHandler.stop(); + break; + case 'status': + logger.log("Note: 'nupst status' is deprecated. Use 'nupst service status' instead."); + await serviceHandler.status(); + break; + case 'logs': + logger.log("Note: 'nupst logs' is deprecated. Use 'nupst service logs' instead."); + await serviceHandler.logs(); + break; case 'daemon-start': + logger.log("Note: 'nupst daemon-start' is deprecated. Use 'nupst service start-daemon' instead."); 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; - + // Top-level commands (no changes) case 'update': await serviceHandler.update(); break; - case 'uninstall': await serviceHandler.uninstall(); break; - - case 'config': - await this.showConfig(); - break; - case 'help': + case '--help': + case '-h': + this.showHelp(); + break; default: + logger.error(`Unknown command: ${command}`); + logger.log(''); this.showHelp(); break; } @@ -328,45 +427,123 @@ export class NupstCli { } } + /** + * Display version information + */ + private showVersion(): void { + const version = this.nupst.getVersion(); + logger.log(`NUPST version ${version}`); + logger.log('Deno-powered UPS monitoring tool'); + } + /** * Display help message */ private showHelp(): void { logger.log(` -NUPST - Node.js UPS Shutdown Tool +NUPST - 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 + nupst [options] -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 +Commands: + service - Manage systemd service + ups - Manage UPS devices + group - Manage UPS groups + config [show] - Display current configuration + update - Update NUPST from repository (requires root) + uninstall - Completely remove NUPST from system (requires root) + help, --help, -h - Show this help message + --version, -v - Show version information + +Service Subcommands: + nupst service enable - Install and enable systemd service (requires root) + nupst service disable - Stop and disable systemd service (requires root) + nupst service start - Start the systemd service + nupst service stop - Stop the systemd service + nupst service restart - Restart the systemd service + nupst service status - Show service and UPS status + nupst service logs - Show service logs in real-time + nupst service start-daemon - Start daemon process directly + +UPS Subcommands: + nupst ups add - Add a new UPS device + nupst ups edit [id] - Edit a UPS device (default if no ID) + nupst ups remove - Remove a UPS device by ID + nupst ups list (or ls) - List all configured UPS devices + nupst ups test - Test UPS connections + +Group Subcommands: + nupst group add - Add a new UPS group + nupst group edit - Edit an existing UPS group + nupst group remove - Remove a UPS group by ID + nupst group list (or ls) - List all UPS groups Options: - --debug, -d - Enable debug mode for detailed SNMP logging - (Example: nupst test --debug) + --debug, -d - Enable debug mode for detailed SNMP logging + (Example: nupst ups test --debug) + +Examples: + nupst service enable - Install and start the service + nupst ups add - Add a new UPS interactively + nupst group list - Show all configured groups + nupst config - Display current configuration + +Note: Old command format (e.g., 'nupst add') still works but is deprecated. + Use the new format (e.g., 'nupst ups add') going forward. +`); + } + + /** + * Display help message for service commands + */ + private showServiceHelp(): void { + logger.log(` +NUPST - Service Management Commands + +Usage: + nupst service + +Subcommands: + enable - Install and enable the systemd service (requires root) + disable - Stop and disable the systemd service (requires root) + start - Start the systemd service + stop - Stop the systemd service + restart - Restart the systemd service + status - Show service status and UPS information + logs - Show service logs in real-time + start-daemon - Start the daemon process directly (for testing) + +Options: + --debug, -d - Enable debug mode for detailed logging +`); + } + + /** + * Display help message for UPS commands + */ + private showUpsHelp(): void { + logger.log(` +NUPST - UPS Management Commands + +Usage: + nupst ups [arguments] + +Subcommands: + add - Add a new UPS device interactively + edit [id] - Edit a UPS device (edits default if no ID provided) + remove - Remove a UPS device by ID (alias: rm) + list - List all configured UPS devices (alias: ls) + test - Test connections to all configured UPS devices + +Options: + --debug, -d - Enable debug mode for detailed SNMP logging + +Examples: + nupst ups add - Add a new UPS device + nupst ups edit ups-1 - Edit UPS with ID 'ups-1' + nupst ups remove ups-1 - Remove UPS with ID 'ups-1' + nupst ups test --debug - Test all UPS connections with debug output `); } @@ -378,13 +555,21 @@ Options: 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 + nupst group [arguments] + +Subcommands: + add - Add a new UPS group interactively + edit - Edit an existing UPS group + remove - Remove a UPS group by ID (alias: rm) + list - List all UPS groups (alias: ls) Options: - --debug, -d - Enable debug mode for detailed logging + --debug, -d - Enable debug mode for detailed logging + +Examples: + nupst group add - Create a new group + nupst group edit dc-1 - Edit group with ID 'dc-1' + nupst group remove dc-1 - Remove group with ID 'dc-1' `); } } \ No newline at end of file diff --git a/ts/cli/group-handler.ts b/ts/cli/group-handler.ts index 9790c41..5dcb5b4 100644 --- a/ts/cli/group-handler.ts +++ b/ts/cli/group-handler.ts @@ -88,7 +88,7 @@ export class GroupHandler { public async add(): Promise { try { // Import readline module for user input - const readline = await import('readline'); + const readline = await import('node:readline'); const rl = readline.createInterface({ input: process.stdin, @@ -198,7 +198,7 @@ export class GroupHandler { public async edit(groupId: string): Promise { try { // Import readline module for user input - const readline = await import('readline'); + const readline = await import('node:readline'); const rl = readline.createInterface({ input: process.stdin, @@ -306,7 +306,7 @@ export class GroupHandler { * Delete an existing UPS group * @param groupId ID of the group to delete */ - public async delete(groupId: string): Promise { + public async remove(groupId: string): Promise { try { // Try to load configuration try { @@ -335,7 +335,7 @@ export class GroupHandler { const groupToDelete = config.groups[groupIndex]; // Get confirmation before deleting - const readline = await import('readline'); + const readline = await import('node:readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, diff --git a/ts/cli/ups-handler.ts b/ts/cli/ups-handler.ts index cd6580c..371c4ca 100644 --- a/ts/cli/ups-handler.ts +++ b/ts/cli/ups-handler.ts @@ -24,7 +24,7 @@ export class UpsHandler { public async add(): Promise { try { // Import readline module for user input - const readline = await import('readline'); + const readline = await import('node:readline'); const rl = readline.createInterface({ input: process.stdin, @@ -155,7 +155,7 @@ export class UpsHandler { public async edit(upsId?: string): Promise { try { // Import readline module for user input - const readline = await import('readline'); + const readline = await import('node:readline'); const rl = readline.createInterface({ input: process.stdin, @@ -284,7 +284,7 @@ export class UpsHandler { * Delete a UPS by ID * @param upsId ID of the UPS to delete */ - public async delete(upsId: string): Promise { + public async remove(upsId: string): Promise { try { // Try to load configuration try { @@ -318,7 +318,7 @@ export class UpsHandler { const upsToDelete = config.upsDevices[upsIndex]; // Get confirmation before deleting - const readline = await import('readline'); + const readline = await import('node:readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout,