diff --git a/ts/cli/group-handler.ts b/ts/cli/group-handler.ts index 75930da..46cbbf6 100644 --- a/ts/cli/group-handler.ts +++ b/ts/cli/group-handler.ts @@ -192,6 +192,7 @@ export class GroupHandler { logger.log('\nGroup setup complete!'); } finally { rl.close(); + process.stdin.destroy(); } } catch (error) { logger.error(`Add group error: ${error instanceof Error ? error.message : String(error)}`); @@ -309,6 +310,7 @@ export class GroupHandler { logger.log('\nGroup edit complete!'); } finally { rl.close(); + process.stdin.destroy(); } } catch (error) { logger.error(`Edit group error: ${error instanceof Error ? error.message : String(error)}`); @@ -366,6 +368,7 @@ export class GroupHandler { }); rl.close(); + process.stdin.destroy(); if (confirm !== 'y' && confirm !== 'yes') { logger.log('Deletion cancelled.'); diff --git a/ts/cli/service-handler.ts b/ts/cli/service-handler.ts index 725ef81..4271f65 100644 --- a/ts/cli/service-handler.ts +++ b/ts/cli/service-handler.ts @@ -213,9 +213,11 @@ export class ServiceHandler { }); }; - console.log('\nNUPST Uninstaller'); - console.log('==============='); - console.log('This will completely remove NUPST from your system.\n'); + logger.log(''); + logger.highlight('NUPST Uninstaller'); + logger.dim('==============='); + logger.log('This will completely remove NUPST from your system.'); + logger.log(''); // Ask about removing configuration const removeConfig = await prompt( @@ -251,17 +253,20 @@ export class ServiceHandler { } if (!uninstallScriptPath) { - console.error('Could not locate uninstall.sh script. Aborting uninstall.'); + logger.error('Could not locate uninstall.sh script. Aborting uninstall.'); rl.close(); + process.stdin.destroy(); process.exit(1); } } // Close readline before executing script rl.close(); + process.stdin.destroy(); // Execute uninstall.sh with the appropriate option - console.log(`\nRunning uninstaller from ${uninstallScriptPath}...`); + logger.log(''); + logger.log(`Running uninstaller from ${uninstallScriptPath}...`); // Pass the configuration removal option as an environment variable const env = { @@ -277,7 +282,7 @@ export class ServiceHandler { stdio: 'inherit', // Show output in the terminal }); } catch (error) { - console.error(`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`); + logger.error(`Uninstall failed: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } } diff --git a/ts/cli/ups-handler.ts b/ts/cli/ups-handler.ts index c7aa2f1..1a4c801 100644 --- a/ts/cli/ups-handler.ts +++ b/ts/cli/ups-handler.ts @@ -47,6 +47,7 @@ export class UpsHandler { await this.runAddProcess(prompt); } finally { rl.close(); + process.stdin.destroy(); } } catch (error) { logger.error(`Add UPS error: ${error instanceof Error ? error.message : String(error)}`); @@ -178,6 +179,7 @@ export class UpsHandler { await this.runEditProcess(upsId, prompt); } finally { rl.close(); + process.stdin.destroy(); } } catch (error) { logger.error(`Edit UPS error: ${error instanceof Error ? error.message : String(error)}`); @@ -344,6 +346,7 @@ export class UpsHandler { }); rl.close(); + process.stdin.destroy(); if (confirm !== 'y' && confirm !== 'yes') { logger.log('Deletion cancelled.'); @@ -667,10 +670,11 @@ export class UpsHandler { // SNMP Version const defaultVersion = snmpConfig.version || 1; - console.log('\nSNMP Version:'); - console.log(' 1) SNMPv1'); - console.log(' 2) SNMPv2c'); - console.log(' 3) SNMPv3 (with security features)'); + logger.log(''); + logger.info('SNMP Version:'); + logger.dim(' 1) SNMPv1'); + logger.dim(' 2) SNMPv2c'); + logger.dim(' 3) SNMPv3 (with security features)'); const versionInput = await prompt(`Select SNMP version [${defaultVersion}]: `); const version = parseInt(versionInput, 10); snmpConfig.version = versionInput.trim() && (version === 1 || version === 2 || version === 3) @@ -697,13 +701,15 @@ export class UpsHandler { snmpConfig: any, prompt: (question: string) => Promise, ): Promise { - console.log('\nSNMPv3 Security Settings:'); + logger.log(''); + logger.info('SNMPv3 Security Settings:'); // Security Level - console.log('\nSecurity Level:'); - console.log(' 1) noAuthNoPriv (No Authentication, No Privacy)'); - console.log(' 2) authNoPriv (Authentication, No Privacy)'); - console.log(' 3) authPriv (Authentication and Privacy)'); + logger.log(''); + logger.info('Security Level:'); + logger.dim(' 1) noAuthNoPriv (No Authentication, No Privacy)'); + logger.dim(' 2) authNoPriv (Authentication, No Privacy)'); + logger.dim(' 3) authPriv (Authentication and Privacy)'); const defaultSecLevel = snmpConfig.securityLevel ? snmpConfig.securityLevel === 'noAuthNoPriv' ? 1 @@ -752,8 +758,9 @@ export class UpsHandler { // Allow customizing the timeout value const defaultTimeout = snmpConfig.timeout / 1000; // Convert from ms to seconds for display - console.log( - '\nSNMPv3 operations with authentication and privacy may require longer timeouts.', + logger.log(''); + logger.info( + 'SNMPv3 operations with authentication and privacy may require longer timeouts.', ); const timeoutInput = await prompt(`SNMP Timeout in seconds [${defaultTimeout}]: `); const timeout = parseInt(timeoutInput, 10); @@ -773,9 +780,10 @@ export class UpsHandler { prompt: (question: string) => Promise, ): Promise { // Authentication protocol - console.log('\nAuthentication Protocol:'); - console.log(' 1) MD5'); - console.log(' 2) SHA'); + logger.log(''); + logger.info('Authentication Protocol:'); + logger.dim(' 1) MD5'); + logger.dim(' 2) SHA'); const defaultAuthProtocol = snmpConfig.authProtocol === 'SHA' ? 2 : 1; const authProtocolInput = await prompt( `Select Authentication Protocol [${defaultAuthProtocol}]: `, @@ -799,9 +807,10 @@ export class UpsHandler { prompt: (question: string) => Promise, ): Promise { // Privacy protocol - console.log('\nPrivacy Protocol:'); - console.log(' 1) DES'); - console.log(' 2) AES'); + logger.log(''); + logger.info('Privacy Protocol:'); + logger.dim(' 1) DES'); + logger.dim(' 2) AES'); const defaultPrivProtocol = snmpConfig.privProtocol === 'AES' ? 2 : 1; const privProtocolInput = await prompt(`Select Privacy Protocol [${defaultPrivProtocol}]: `); const privProtocol = parseInt(privProtocolInput, 10) || defaultPrivProtocol; @@ -822,7 +831,8 @@ export class UpsHandler { thresholds: any, prompt: (question: string) => Promise, ): Promise { - console.log('\nShutdown Thresholds:'); + logger.log(''); + logger.info('Shutdown Thresholds:'); // Battery threshold const defaultBatteryThreshold = thresholds.battery || 60; @@ -854,13 +864,14 @@ export class UpsHandler { snmpConfig: any, prompt: (question: string) => Promise, ): Promise { - console.log('\nUPS Model Selection:'); - console.log(' 1) CyberPower'); - console.log(' 2) APC'); - console.log(' 3) Eaton'); - console.log(' 4) TrippLite'); - console.log(' 5) Liebert/Vertiv'); - console.log(' 6) Custom (Advanced)'); + logger.log(''); + logger.info('UPS Model Selection:'); + logger.dim(' 1) CyberPower'); + logger.dim(' 2) APC'); + logger.dim(' 3) Eaton'); + logger.dim(' 4) TrippLite'); + logger.dim(' 5) Liebert/Vertiv'); + logger.dim(' 6) Custom (Advanced)'); const defaultModelValue = snmpConfig.upsModel === 'cyberpower' ? 1 @@ -891,8 +902,9 @@ export class UpsHandler { snmpConfig.upsModel = 'liebert'; } else if (modelValue === 6) { snmpConfig.upsModel = 'custom'; - console.log('\nEnter custom OIDs for your UPS:'); - console.log('(Leave blank to use standard RFC 1628 OIDs as fallback)'); + logger.log(''); + logger.info('Enter custom OIDs for your UPS:'); + logger.dim('(Leave blank to use standard RFC 1628 OIDs as fallback)'); // Custom OIDs const powerStatusOID = await prompt('Power Status OID: '); diff --git a/ts/daemon.ts b/ts/daemon.ts index bcdcf96..b527bb9 100644 --- a/ts/daemon.ts +++ b/ts/daemon.ts @@ -207,11 +207,9 @@ export class NupstDaemon { fs.writeFileSync(this.CONFIG_PATH, JSON.stringify(configToSave, null, 2)); this.config = configToSave; - console.log('┌─ Configuration Saved ─────────────────────┐'); - console.log(`│ Location: ${this.CONFIG_PATH}`); - console.log('└──────────────────────────────────────────┘'); + logger.logBox('Configuration Saved', [`Location: ${this.CONFIG_PATH}`], 45, 'success'); } catch (error) { - console.error('Error saving configuration:', error); + logger.error(`Error saving configuration: ${error}`); } } @@ -219,10 +217,7 @@ export class NupstDaemon { * Helper method to log configuration errors consistently */ private logConfigError(message: string): void { - console.error('┌─ Configuration Error ─────────────────────┐'); - console.error(`│ ${message}`); - console.error("│ Please run 'nupst setup' first to create a configuration."); - console.error('└───────────────────────────────────────────┘'); + logger.logBox('Configuration Error', [message, "Please run 'nupst setup' first to create a configuration."], 45, 'error'); } /** diff --git a/ts/migrations/migration-runner.ts b/ts/migrations/migration-runner.ts index 1c26572..0578397 100644 --- a/ts/migrations/migration-runner.ts +++ b/ts/migrations/migration-runner.ts @@ -34,12 +34,14 @@ export class MigrationRunner { let currentConfig = config; let anyMigrationsRan = false; - logger.dim('Checking for required config migrations...'); - for (const migration of this.migrations) { const shouldRun = await migration.shouldRun(currentConfig); if (shouldRun) { + // Only show "checking" message when we actually need to migrate + if (!anyMigrationsRan) { + logger.dim('Checking for required config migrations...'); + } logger.info(`Running ${migration.getName()}...`); currentConfig = await migration.migrate(currentConfig); anyMigrationsRan = true; @@ -49,7 +51,7 @@ export class MigrationRunner { if (anyMigrationsRan) { logger.success('Configuration migrations complete'); } else { - logger.dim('No migrations needed'); + logger.success('config format ok'); } return { diff --git a/ts/systemd.ts b/ts/systemd.ts index 1bdbeca..7602342 100644 --- a/ts/systemd.ts +++ b/ts/systemd.ts @@ -50,11 +50,11 @@ WantedBy=multi-user.target try { await fs.access(configPath); } catch (error) { - console.log(''); - console.log(`${symbols.error} ${theme.error('No configuration found')}`); - console.log(` ${theme.dim('Config file:')} ${configPath}`); - console.log(` ${theme.dim('Run')} ${theme.command('nupst ups add')} ${theme.dim('to create a configuration')}`); - console.log(''); + logger.log(''); + logger.error('No configuration found'); + logger.log(` ${theme.dim('Config file:')} ${configPath}`); + logger.log(` ${theme.dim('Run')} ${theme.command('nupst ups add')} ${theme.dim('to create a configuration')}`); + logger.log(''); throw new Error('Configuration not found'); } } @@ -192,11 +192,11 @@ WantedBy=multi-user.target } // Display beautiful status - console.log(''); + logger.log(''); if (isActive) { - console.log(`${symbols.running} ${theme.success('Service:')} ${theme.statusActive('active (running)')}`); + logger.log(`${symbols.running} ${theme.success('Service:')} ${theme.statusActive('active (running)')}`); } else { - console.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('inactive')}`); + logger.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('inactive')}`); } if (pid || memory || cpu) { @@ -204,14 +204,14 @@ WantedBy=multi-user.target if (pid) details.push(`PID: ${theme.dim(pid)}`); if (memory) details.push(`Memory: ${theme.dim(memory)}`); if (cpu) details.push(`CPU: ${theme.dim(cpu)}`); - console.log(` ${details.join(' ')}`); + logger.log(` ${details.join(' ')}`); } - console.log(''); + logger.log(''); } catch (error) { - console.log(''); - console.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('not installed')}`); - console.log(''); + logger.log(''); + logger.log(`${symbols.stopped} ${theme.dim('Service:')} ${theme.statusInactive('not installed')}`); + logger.log(''); } } @@ -228,7 +228,7 @@ WantedBy=multi-user.target // Check if we have the new multi-UPS config format if (config.upsDevices && Array.isArray(config.upsDevices) && config.upsDevices.length > 0) { - console.log(theme.info(`UPS Devices (${config.upsDevices.length}):`)); + logger.info(`UPS Devices (${config.upsDevices.length}):`); // Show status for each UPS for (const ups of config.upsDevices) { @@ -236,7 +236,7 @@ WantedBy=multi-user.target } } else if (config.snmp) { // Legacy single UPS configuration - console.log(theme.info('UPS Devices (1):')); + logger.info('UPS Devices (1):'); const legacyUps = { id: 'default', name: 'Default UPS', @@ -247,16 +247,16 @@ WantedBy=multi-user.target await this.displaySingleUpsStatus(legacyUps, snmp); } else { - console.log(''); - console.log(`${symbols.warning} ${theme.warning('No UPS devices configured')}`); - console.log(` ${theme.dim('Run')} ${theme.command('nupst ups add')} ${theme.dim('to add a device')}`); - console.log(''); + logger.log(''); + logger.warn('No UPS devices configured'); + logger.log(` ${theme.dim('Run')} ${theme.command('nupst ups add')} ${theme.dim('to add a device')}`); + logger.log(''); } } catch (error) { - console.log(''); - console.log(`${symbols.error} ${theme.error('Failed to retrieve UPS status')}`); - console.log(` ${theme.dim(error instanceof Error ? error.message : String(error))}`); - console.log(''); + logger.log(''); + logger.error('Failed to retrieve UPS status'); + logger.log(` ${theme.dim(error instanceof Error ? error.message : String(error))}`); + logger.log(''); } } @@ -284,15 +284,15 @@ WantedBy=multi-user.target } // Display UPS name and power status - console.log(` ${statusSymbol} ${theme.highlight(ups.name)} - ${formatPowerStatus(status.powerStatus)}`); + logger.log(` ${statusSymbol} ${theme.highlight(ups.name)} - ${formatPowerStatus(status.powerStatus)}`); // Display battery with color coding const batteryColor = getBatteryColor(status.batteryCapacity); const batterySymbol = status.batteryCapacity >= ups.thresholds.battery ? symbols.success : symbols.warning; - console.log(` Battery: ${batteryColor(status.batteryCapacity + '%')} ${batterySymbol} Runtime: ${getRuntimeColor(status.batteryRuntime)(status.batteryRuntime + ' min')}`); + logger.log(` Battery: ${batteryColor(status.batteryCapacity + '%')} ${batterySymbol} Runtime: ${getRuntimeColor(status.batteryRuntime)(status.batteryRuntime + ' min')}`); // Display host info - console.log(` ${theme.dim(`Host: ${ups.snmp.host}:${ups.snmp.port}`)}`); + logger.log(` ${theme.dim(`Host: ${ups.snmp.host}:${ups.snmp.port}`)}`); // Display groups if any if (ups.groups && ups.groups.length > 0) { @@ -301,17 +301,17 @@ WantedBy=multi-user.target const group = config.groups?.find((g: { id: string }) => g.id === groupId); return group ? group.name : groupId; }); - console.log(` ${theme.dim(`Groups: ${groupNames.join(', ')}`)}`); + logger.log(` ${theme.dim(`Groups: ${groupNames.join(', ')}`)}`); } - console.log(''); + logger.log(''); } catch (error) { // Display error for this UPS - console.log(` ${symbols.error} ${theme.highlight(ups.name)} - ${theme.error('Connection failed')}`); - console.log(` ${theme.dim(error instanceof Error ? error.message : String(error))}`); - console.log(` ${theme.dim(`Host: ${ups.snmp.host}:${ups.snmp.port}`)}`); - console.log(''); + logger.log(` ${symbols.error} ${theme.highlight(ups.name)} - ${theme.error('Connection failed')}`); + logger.log(` ${theme.dim(error instanceof Error ? error.message : String(error))}`); + logger.log(` ${theme.dim(`Host: ${ups.snmp.host}:${ups.snmp.port}`)}`); + logger.log(''); } }