|
|
|
@ -30,7 +30,7 @@ export class NupstCli {
|
|
|
|
|
// Enable debug mode in the SNMP client
|
|
|
|
|
this.nupst.getSnmp().enableDebug();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get the command (default to help if none provided)
|
|
|
|
|
const command = args[2] || 'help';
|
|
|
|
|
|
|
|
|
@ -46,8 +46,8 @@ export class NupstCli {
|
|
|
|
|
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');
|
|
|
|
|
|
|
|
|
|
const cleanedArgs = args.filter((arg) => arg !== '--debug' && arg !== '-d');
|
|
|
|
|
|
|
|
|
|
return { debugMode, cleanedArgs };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -85,23 +85,23 @@ export class NupstCli {
|
|
|
|
|
case 'disable':
|
|
|
|
|
await this.disable();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'setup':
|
|
|
|
|
await this.setup();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'test':
|
|
|
|
|
await this.test(debugMode);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'update':
|
|
|
|
|
await this.update();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'uninstall':
|
|
|
|
|
await this.uninstall();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case 'config':
|
|
|
|
|
await this.showConfig();
|
|
|
|
|
break;
|
|
|
|
@ -149,17 +149,17 @@ export class NupstCli {
|
|
|
|
|
// Use exec with spawn to properly follow logs in real-time
|
|
|
|
|
const { spawn } = await import('child_process');
|
|
|
|
|
console.log('Tailing nupst service logs (Ctrl+C to exit)...\n');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const journalctl = spawn('journalctl', ['-u', 'nupst.service', '-n', '50', '-f'], {
|
|
|
|
|
stdio: ['ignore', 'inherit', 'inherit']
|
|
|
|
|
stdio: ['ignore', 'inherit', 'inherit'],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Forward signals to child process
|
|
|
|
|
process.on('SIGINT', () => {
|
|
|
|
|
journalctl.kill('SIGINT');
|
|
|
|
|
process.exit(0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Wait for process to exit
|
|
|
|
|
await new Promise<void>((resolve) => {
|
|
|
|
|
journalctl.on('exit', () => resolve());
|
|
|
|
@ -229,21 +229,21 @@ export class NupstCli {
|
|
|
|
|
console.log('│ SNMP debugging enabled - detailed logs will be shown');
|
|
|
|
|
console.log('└──────────────────────────────────────────┘');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Try to load the configuration
|
|
|
|
|
try {
|
|
|
|
|
await this.nupst.getDaemon().loadConfig();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('┌─ Configuration Error ─────────────────────┐');
|
|
|
|
|
console.error('│ No configuration found.');
|
|
|
|
|
console.error('│ Please run \'nupst setup\' first to create a configuration.');
|
|
|
|
|
console.error("│ Please run 'nupst setup' first to create a configuration.");
|
|
|
|
|
console.error('└──────────────────────────────────────────┘');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get current configuration
|
|
|
|
|
const config = this.nupst.getDaemon().getConfig();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.displayTestConfig(config);
|
|
|
|
|
await this.testConnection(config);
|
|
|
|
|
} catch (error) {
|
|
|
|
@ -262,26 +262,26 @@ export class NupstCli {
|
|
|
|
|
console.log(`│ Port: ${config.snmp.port}`);
|
|
|
|
|
console.log(`│ Version: ${config.snmp.version}`);
|
|
|
|
|
console.log(`│ UPS Model: ${config.snmp.upsModel || 'cyberpower'}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (config.snmp.version === 1 || config.snmp.version === 2) {
|
|
|
|
|
console.log(`│ Community: ${config.snmp.community}`);
|
|
|
|
|
} else if (config.snmp.version === 3) {
|
|
|
|
|
console.log(`│ Security Level: ${config.snmp.securityLevel}`);
|
|
|
|
|
console.log(`│ Username: ${config.snmp.username}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Show auth and privacy details based on security level
|
|
|
|
|
if (config.snmp.securityLevel === 'authNoPriv' || config.snmp.securityLevel === 'authPriv') {
|
|
|
|
|
console.log(`│ Auth Protocol: ${config.snmp.authProtocol || 'None'}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (config.snmp.securityLevel === 'authPriv') {
|
|
|
|
|
console.log(`│ Privacy Protocol: ${config.snmp.privProtocol || 'None'}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Show timeout value
|
|
|
|
|
console.log(`│ Timeout: ${config.snmp.timeout / 1000} seconds`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Show OIDs if custom model is selected
|
|
|
|
|
if (config.snmp.upsModel === 'custom' && config.snmp.customOIDs) {
|
|
|
|
|
console.log('│ Custom OIDs:');
|
|
|
|
@ -304,20 +304,20 @@ export class NupstCli {
|
|
|
|
|
console.log('\nTesting connection to UPS...');
|
|
|
|
|
try {
|
|
|
|
|
// Create a test config with a short timeout
|
|
|
|
|
const testConfig = {
|
|
|
|
|
const testConfig = {
|
|
|
|
|
...config.snmp,
|
|
|
|
|
timeout: Math.min(config.snmp.timeout, 10000) // Use at most 10 seconds for testing
|
|
|
|
|
timeout: Math.min(config.snmp.timeout, 10000), // Use at most 10 seconds for testing
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const status = await this.nupst.getSnmp().getUpsStatus(testConfig);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('┌─ Connection Successful! ─────────────────┐');
|
|
|
|
|
console.log('│ UPS Status:');
|
|
|
|
|
console.log(`│ Power Status: ${status.powerStatus}`);
|
|
|
|
|
console.log(`│ Battery Capacity: ${status.batteryCapacity}%`);
|
|
|
|
|
console.log(`│ Runtime Remaining: ${status.batteryRuntime} minutes`);
|
|
|
|
|
console.log('└──────────────────────────────────────────┘');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check status against thresholds if on battery
|
|
|
|
|
if (status.powerStatus === 'onBattery') {
|
|
|
|
|
this.analyzeThresholds(status, config);
|
|
|
|
@ -326,7 +326,7 @@ export class NupstCli {
|
|
|
|
|
console.error('┌─ Connection Failed! ───────────────────────┐');
|
|
|
|
|
console.error(`│ Error: ${error.message}`);
|
|
|
|
|
console.error('└──────────────────────────────────────────┘');
|
|
|
|
|
console.log('\nPlease check your settings and run \'nupst setup\' to reconfigure.');
|
|
|
|
|
console.log("\nPlease check your settings and run 'nupst setup' to reconfigure.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -337,25 +337,33 @@ export class NupstCli {
|
|
|
|
|
*/
|
|
|
|
|
private analyzeThresholds(status: any, config: any): void {
|
|
|
|
|
console.log('┌─ Threshold Analysis ───────────────────────┐');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (status.batteryCapacity < config.thresholds.battery) {
|
|
|
|
|
console.log('│ ⚠️ WARNING: Battery capacity below threshold');
|
|
|
|
|
console.log(`│ Current: ${status.batteryCapacity}% | Threshold: ${config.thresholds.battery}%`);
|
|
|
|
|
console.log(
|
|
|
|
|
`│ Current: ${status.batteryCapacity}% | Threshold: ${config.thresholds.battery}%`
|
|
|
|
|
);
|
|
|
|
|
console.log('│ System would initiate shutdown');
|
|
|
|
|
} else {
|
|
|
|
|
console.log('│ ✓ Battery capacity above threshold');
|
|
|
|
|
console.log(`│ Current: ${status.batteryCapacity}% | Threshold: ${config.thresholds.battery}%`);
|
|
|
|
|
console.log(
|
|
|
|
|
`│ Current: ${status.batteryCapacity}% | Threshold: ${config.thresholds.battery}%`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (status.batteryRuntime < config.thresholds.runtime) {
|
|
|
|
|
console.log('│ ⚠️ WARNING: Runtime below threshold');
|
|
|
|
|
console.log(`│ Current: ${status.batteryRuntime} min | Threshold: ${config.thresholds.runtime} min`);
|
|
|
|
|
console.log(
|
|
|
|
|
`│ Current: ${status.batteryRuntime} min | Threshold: ${config.thresholds.runtime} min`
|
|
|
|
|
);
|
|
|
|
|
console.log('│ System would initiate shutdown');
|
|
|
|
|
} else {
|
|
|
|
|
console.log('│ ✓ Runtime above threshold');
|
|
|
|
|
console.log(`│ Current: ${status.batteryRuntime} min | Threshold: ${config.thresholds.runtime} min`);
|
|
|
|
|
console.log(
|
|
|
|
|
`│ Current: ${status.batteryRuntime} min | Threshold: ${config.thresholds.runtime} min`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('└──────────────────────────────────────────┘');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -386,60 +394,72 @@ Options:
|
|
|
|
|
(Example: nupst test --debug)
|
|
|
|
|
`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Update NUPST from repository and refresh systemd service
|
|
|
|
|
*/
|
|
|
|
|
private async update(): Promise<void> {
|
|
|
|
|
try {
|
|
|
|
|
// Check if running as root
|
|
|
|
|
this.checkRootAccess('This command must be run as root to update NUPST and refresh the systemd service.');
|
|
|
|
|
|
|
|
|
|
this.checkRootAccess(
|
|
|
|
|
'This command must be run as root to update NUPST and refresh the systemd service.'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
console.log('┌─ NUPST Update Process ──────────────────┐');
|
|
|
|
|
console.log('│ Updating NUPST from repository...');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Determine the installation directory (assuming it's either /opt/nupst or the current directory)
|
|
|
|
|
const { existsSync } = await import('fs');
|
|
|
|
|
let installDir = '/opt/nupst';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!existsSync(installDir)) {
|
|
|
|
|
// If not installed in /opt/nupst, use the current directory
|
|
|
|
|
const { dirname } = await import('path');
|
|
|
|
|
installDir = dirname(dirname(process.argv[1])); // Go up two levels from the executable
|
|
|
|
|
console.log(`│ Using local installation directory: ${installDir}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 1. Update the repository
|
|
|
|
|
console.log('│ Pulling latest changes from git repository...');
|
|
|
|
|
execSync(`cd ${installDir} && git fetch origin && git reset --hard origin/main`, { stdio: 'pipe' });
|
|
|
|
|
|
|
|
|
|
execSync(`cd ${installDir} && git fetch origin && git reset --hard origin/main`, {
|
|
|
|
|
stdio: 'pipe',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 2. Run the install.sh script
|
|
|
|
|
console.log('│ Running install.sh to update NUPST...');
|
|
|
|
|
execSync(`cd ${installDir} && bash ./install.sh`, { stdio: 'pipe' });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. Run the setup.sh script with force flag to update Node.js and dependencies
|
|
|
|
|
console.log('│ Running setup.sh to update Node.js and dependencies...');
|
|
|
|
|
execSync(`cd ${installDir} && bash ./setup.sh --force`, { stdio: 'pipe' });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. Refresh the systemd service
|
|
|
|
|
console.log('│ Refreshing systemd service...');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// First check if service exists
|
|
|
|
|
const serviceExists = execSync('systemctl list-unit-files | grep nupst.service').toString().includes('nupst.service');
|
|
|
|
|
|
|
|
|
|
let serviceExists = false;
|
|
|
|
|
try {
|
|
|
|
|
const output = execSync('systemctl list-unit-files | grep nupst.service').toString();
|
|
|
|
|
serviceExists = output.includes('nupst.service');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// If grep fails (service not found), serviceExists remains false
|
|
|
|
|
serviceExists = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (serviceExists) {
|
|
|
|
|
// Stop the service if it's running
|
|
|
|
|
const isRunning = execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
|
|
|
|
|
const isRunning =
|
|
|
|
|
execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
|
|
|
|
|
if (isRunning) {
|
|
|
|
|
console.log('│ Stopping nupst service...');
|
|
|
|
|
execSync('systemctl stop nupst.service');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reinstall the service
|
|
|
|
|
console.log('│ Reinstalling systemd service...');
|
|
|
|
|
await this.nupst.getSystemd().install();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Restart the service if it was running
|
|
|
|
|
if (isRunning) {
|
|
|
|
|
console.log('│ Restarting nupst service...');
|
|
|
|
@ -449,7 +469,7 @@ Options:
|
|
|
|
|
console.log('│ Systemd service not installed, skipping service refresh.');
|
|
|
|
|
console.log('│ Run "nupst enable" to install the service.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('│ Update completed successfully!');
|
|
|
|
|
console.log('└──────────────────────────────────────────┘');
|
|
|
|
|
} catch (error) {
|
|
|
|
@ -463,7 +483,7 @@ Options:
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Interactive setup for configuring SNMP settings
|
|
|
|
|
*/
|
|
|
|
@ -471,12 +491,12 @@ Options:
|
|
|
|
|
try {
|
|
|
|
|
// Import readline module (ESM style)
|
|
|
|
|
const readline = await import('readline');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const rl = readline.createInterface({
|
|
|
|
|
input: process.stdin,
|
|
|
|
|
output: process.stdout
|
|
|
|
|
output: process.stdout,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Helper function to prompt for input
|
|
|
|
|
const prompt = (question: string): Promise<string> => {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
@ -485,7 +505,7 @@ Options:
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await this.runSetupProcess(prompt);
|
|
|
|
|
} finally {
|
|
|
|
@ -504,7 +524,7 @@ Options:
|
|
|
|
|
console.log('\nNUPST Interactive Setup');
|
|
|
|
|
console.log('======================\n');
|
|
|
|
|
console.log('This will guide you through configuring your UPS SNMP settings.\n');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Try to load existing config if available
|
|
|
|
|
let config;
|
|
|
|
|
try {
|
|
|
|
@ -518,24 +538,24 @@ Options:
|
|
|
|
|
|
|
|
|
|
// Gather SNMP settings
|
|
|
|
|
config = await this.gatherSnmpSettings(config, prompt);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Gather threshold settings
|
|
|
|
|
config = await this.gatherThresholdSettings(config, prompt);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Gather UPS model settings
|
|
|
|
|
config = await this.gatherUpsModelSettings(config, prompt);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Save the configuration
|
|
|
|
|
await this.nupst.getDaemon().saveConfig(config);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.displayConfigSummary(config);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Test the connection if requested
|
|
|
|
|
await this.optionallyTestConnection(config, prompt);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if service is running and restart it if needed
|
|
|
|
|
await this.restartServiceIfRunning();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('\nSetup complete!');
|
|
|
|
|
await this.optionallyEnableService(prompt);
|
|
|
|
|
}
|
|
|
|
@ -546,18 +566,21 @@ Options:
|
|
|
|
|
* @param prompt Function to prompt for user input
|
|
|
|
|
* @returns Updated configuration
|
|
|
|
|
*/
|
|
|
|
|
private async gatherSnmpSettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> {
|
|
|
|
|
private async gatherSnmpSettings(
|
|
|
|
|
config: any,
|
|
|
|
|
prompt: (question: string) => Promise<string>
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
// SNMP IP Address
|
|
|
|
|
const defaultHost = config.snmp.host;
|
|
|
|
|
const host = await prompt(`UPS IP Address [${defaultHost}]: `);
|
|
|
|
|
config.snmp.host = host.trim() || defaultHost;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SNMP Port
|
|
|
|
|
const defaultPort = config.snmp.port;
|
|
|
|
|
const portInput = await prompt(`SNMP Port [${defaultPort}]: `);
|
|
|
|
|
const port = parseInt(portInput, 10);
|
|
|
|
|
config.snmp.port = (portInput.trim() && !isNaN(port)) ? port : defaultPort;
|
|
|
|
|
|
|
|
|
|
config.snmp.port = portInput.trim() && !isNaN(port) ? port : defaultPort;
|
|
|
|
|
|
|
|
|
|
// SNMP Version
|
|
|
|
|
const defaultVersion = config.snmp.version;
|
|
|
|
|
console.log('\nSNMP Version:');
|
|
|
|
@ -566,8 +589,11 @@ Options:
|
|
|
|
|
console.log(' 3) SNMPv3 (with security features)');
|
|
|
|
|
const versionInput = await prompt(`Select SNMP version [${defaultVersion}]: `);
|
|
|
|
|
const version = parseInt(versionInput, 10);
|
|
|
|
|
config.snmp.version = (versionInput.trim() && (version === 1 || version === 2 || version === 3)) ? version : defaultVersion;
|
|
|
|
|
|
|
|
|
|
config.snmp.version =
|
|
|
|
|
versionInput.trim() && (version === 1 || version === 2 || version === 3)
|
|
|
|
|
? version
|
|
|
|
|
: defaultVersion;
|
|
|
|
|
|
|
|
|
|
if (config.snmp.version === 1 || config.snmp.version === 2) {
|
|
|
|
|
// SNMP Community String (for v1/v2c)
|
|
|
|
|
const defaultCommunity = config.snmp.community || 'public';
|
|
|
|
@ -577,7 +603,7 @@ Options:
|
|
|
|
|
// SNMP v3 settings
|
|
|
|
|
config = await this.gatherSnmpV3Settings(config, prompt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -587,20 +613,27 @@ Options:
|
|
|
|
|
* @param prompt Function to prompt for user input
|
|
|
|
|
* @returns Updated configuration
|
|
|
|
|
*/
|
|
|
|
|
private async gatherSnmpV3Settings(config: any, prompt: (question: string) => Promise<string>): Promise<any> {
|
|
|
|
|
private async gatherSnmpV3Settings(
|
|
|
|
|
config: any,
|
|
|
|
|
prompt: (question: string) => Promise<string>
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
console.log('\nSNMPv3 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)');
|
|
|
|
|
const defaultSecLevel = config.snmp.securityLevel ?
|
|
|
|
|
(config.snmp.securityLevel === 'noAuthNoPriv' ? 1 :
|
|
|
|
|
config.snmp.securityLevel === 'authNoPriv' ? 2 : 3) : 3;
|
|
|
|
|
const defaultSecLevel = config.snmp.securityLevel
|
|
|
|
|
? config.snmp.securityLevel === 'noAuthNoPriv'
|
|
|
|
|
? 1
|
|
|
|
|
: config.snmp.securityLevel === 'authNoPriv'
|
|
|
|
|
? 2
|
|
|
|
|
: 3
|
|
|
|
|
: 3;
|
|
|
|
|
const secLevelInput = await prompt(`Select Security Level [${defaultSecLevel}]: `);
|
|
|
|
|
const secLevel = parseInt(secLevelInput, 10) || defaultSecLevel;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (secLevel === 1) {
|
|
|
|
|
config.snmp.securityLevel = 'noAuthNoPriv';
|
|
|
|
|
// No auth, no priv - clear out authentication and privacy settings
|
|
|
|
@ -622,31 +655,33 @@ Options:
|
|
|
|
|
// Set appropriate timeout for security level
|
|
|
|
|
config.snmp.timeout = 15000; // 15 seconds for full encryption
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Username
|
|
|
|
|
const defaultUsername = config.snmp.username || '';
|
|
|
|
|
const username = await prompt(`SNMPv3 Username [${defaultUsername}]: `);
|
|
|
|
|
config.snmp.username = username.trim() || defaultUsername;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (secLevel >= 2) {
|
|
|
|
|
// Authentication settings
|
|
|
|
|
config = await this.gatherAuthenticationSettings(config, prompt);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (secLevel === 3) {
|
|
|
|
|
// Privacy settings
|
|
|
|
|
config = await this.gatherPrivacySettings(config, prompt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Allow customizing the timeout value
|
|
|
|
|
const defaultTimeout = config.snmp.timeout / 1000; // Convert from ms to seconds for display
|
|
|
|
|
console.log('\nSNMPv3 operations with authentication and privacy may require longer timeouts.');
|
|
|
|
|
console.log(
|
|
|
|
|
'\nSNMPv3 operations with authentication and privacy may require longer timeouts.'
|
|
|
|
|
);
|
|
|
|
|
const timeoutInput = await prompt(`SNMP Timeout in seconds [${defaultTimeout}]: `);
|
|
|
|
|
const timeout = parseInt(timeoutInput, 10);
|
|
|
|
|
if (timeoutInput.trim() && !isNaN(timeout)) {
|
|
|
|
|
config.snmp.timeout = timeout * 1000; // Convert to ms
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -656,21 +691,26 @@ Options:
|
|
|
|
|
* @param prompt Function to prompt for user input
|
|
|
|
|
* @returns Updated configuration
|
|
|
|
|
*/
|
|
|
|
|
private async gatherAuthenticationSettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> {
|
|
|
|
|
private async gatherAuthenticationSettings(
|
|
|
|
|
config: any,
|
|
|
|
|
prompt: (question: string) => Promise<string>
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
// Authentication protocol
|
|
|
|
|
console.log('\nAuthentication Protocol:');
|
|
|
|
|
console.log(' 1) MD5');
|
|
|
|
|
console.log(' 2) SHA');
|
|
|
|
|
const defaultAuthProtocol = config.snmp.authProtocol === 'SHA' ? 2 : 1;
|
|
|
|
|
const authProtocolInput = await prompt(`Select Authentication Protocol [${defaultAuthProtocol}]: `);
|
|
|
|
|
const authProtocolInput = await prompt(
|
|
|
|
|
`Select Authentication Protocol [${defaultAuthProtocol}]: `
|
|
|
|
|
);
|
|
|
|
|
const authProtocol = parseInt(authProtocolInput, 10) || defaultAuthProtocol;
|
|
|
|
|
config.snmp.authProtocol = authProtocol === 2 ? 'SHA' : 'MD5';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Authentication Key/Password
|
|
|
|
|
const defaultAuthKey = config.snmp.authKey || '';
|
|
|
|
|
const authKey = await prompt(`Authentication Password ${defaultAuthKey ? '[*****]' : ''}: `);
|
|
|
|
|
config.snmp.authKey = authKey.trim() || defaultAuthKey;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -680,7 +720,10 @@ Options:
|
|
|
|
|
* @param prompt Function to prompt for user input
|
|
|
|
|
* @returns Updated configuration
|
|
|
|
|
*/
|
|
|
|
|
private async gatherPrivacySettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> {
|
|
|
|
|
private async gatherPrivacySettings(
|
|
|
|
|
config: any,
|
|
|
|
|
prompt: (question: string) => Promise<string>
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
// Privacy protocol
|
|
|
|
|
console.log('\nPrivacy Protocol:');
|
|
|
|
|
console.log(' 1) DES');
|
|
|
|
@ -689,12 +732,12 @@ Options:
|
|
|
|
|
const privProtocolInput = await prompt(`Select Privacy Protocol [${defaultPrivProtocol}]: `);
|
|
|
|
|
const privProtocol = parseInt(privProtocolInput, 10) || defaultPrivProtocol;
|
|
|
|
|
config.snmp.privProtocol = privProtocol === 2 ? 'AES' : 'DES';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Privacy Key/Password
|
|
|
|
|
const defaultPrivKey = config.snmp.privKey || '';
|
|
|
|
|
const privKey = await prompt(`Privacy Password ${defaultPrivKey ? '[*****]' : ''}: `);
|
|
|
|
|
config.snmp.privKey = privKey.trim() || defaultPrivKey;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -704,33 +747,43 @@ Options:
|
|
|
|
|
* @param prompt Function to prompt for user input
|
|
|
|
|
* @returns Updated configuration
|
|
|
|
|
*/
|
|
|
|
|
private async gatherThresholdSettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> {
|
|
|
|
|
private async gatherThresholdSettings(
|
|
|
|
|
config: any,
|
|
|
|
|
prompt: (question: string) => Promise<string>
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
console.log('\nShutdown Thresholds:');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Battery threshold
|
|
|
|
|
const defaultBatteryThreshold = config.thresholds.battery;
|
|
|
|
|
const batteryThresholdInput = await prompt(`Battery percentage threshold [${defaultBatteryThreshold}%]: `);
|
|
|
|
|
const batteryThresholdInput = await prompt(
|
|
|
|
|
`Battery percentage threshold [${defaultBatteryThreshold}%]: `
|
|
|
|
|
);
|
|
|
|
|
const batteryThreshold = parseInt(batteryThresholdInput, 10);
|
|
|
|
|
config.thresholds.battery = (batteryThresholdInput.trim() && !isNaN(batteryThreshold))
|
|
|
|
|
? batteryThreshold
|
|
|
|
|
: defaultBatteryThreshold;
|
|
|
|
|
|
|
|
|
|
config.thresholds.battery =
|
|
|
|
|
batteryThresholdInput.trim() && !isNaN(batteryThreshold)
|
|
|
|
|
? batteryThreshold
|
|
|
|
|
: defaultBatteryThreshold;
|
|
|
|
|
|
|
|
|
|
// Runtime threshold
|
|
|
|
|
const defaultRuntimeThreshold = config.thresholds.runtime;
|
|
|
|
|
const runtimeThresholdInput = await prompt(`Runtime minutes threshold [${defaultRuntimeThreshold} minutes]: `);
|
|
|
|
|
const runtimeThresholdInput = await prompt(
|
|
|
|
|
`Runtime minutes threshold [${defaultRuntimeThreshold} minutes]: `
|
|
|
|
|
);
|
|
|
|
|
const runtimeThreshold = parseInt(runtimeThresholdInput, 10);
|
|
|
|
|
config.thresholds.runtime = (runtimeThresholdInput.trim() && !isNaN(runtimeThreshold))
|
|
|
|
|
? runtimeThreshold
|
|
|
|
|
: defaultRuntimeThreshold;
|
|
|
|
|
|
|
|
|
|
config.thresholds.runtime =
|
|
|
|
|
runtimeThresholdInput.trim() && !isNaN(runtimeThreshold)
|
|
|
|
|
? runtimeThreshold
|
|
|
|
|
: defaultRuntimeThreshold;
|
|
|
|
|
|
|
|
|
|
// Check interval
|
|
|
|
|
const defaultInterval = config.checkInterval / 1000; // Convert from ms to seconds for display
|
|
|
|
|
const intervalInput = await prompt(`Check interval in seconds [${defaultInterval}]: `);
|
|
|
|
|
const interval = parseInt(intervalInput, 10);
|
|
|
|
|
config.checkInterval = (intervalInput.trim() && !isNaN(interval))
|
|
|
|
|
? interval * 1000 // Convert to ms
|
|
|
|
|
: defaultInterval * 1000;
|
|
|
|
|
|
|
|
|
|
config.checkInterval =
|
|
|
|
|
intervalInput.trim() && !isNaN(interval)
|
|
|
|
|
? interval * 1000 // Convert to ms
|
|
|
|
|
: defaultInterval * 1000;
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -740,7 +793,10 @@ Options:
|
|
|
|
|
* @param prompt Function to prompt for user input
|
|
|
|
|
* @returns Updated configuration
|
|
|
|
|
*/
|
|
|
|
|
private async gatherUpsModelSettings(config: any, prompt: (question: string) => Promise<string>): Promise<any> {
|
|
|
|
|
private async gatherUpsModelSettings(
|
|
|
|
|
config: any,
|
|
|
|
|
prompt: (question: string) => Promise<string>
|
|
|
|
|
): Promise<any> {
|
|
|
|
|
console.log('\nUPS Model Selection:');
|
|
|
|
|
console.log(' 1) CyberPower');
|
|
|
|
|
console.log(' 2) APC');
|
|
|
|
@ -748,17 +804,25 @@ Options:
|
|
|
|
|
console.log(' 4) TrippLite');
|
|
|
|
|
console.log(' 5) Liebert/Vertiv');
|
|
|
|
|
console.log(' 6) Custom (Advanced)');
|
|
|
|
|
|
|
|
|
|
const defaultModelValue = config.snmp.upsModel === 'cyberpower' ? 1 :
|
|
|
|
|
config.snmp.upsModel === 'apc' ? 2 :
|
|
|
|
|
config.snmp.upsModel === 'eaton' ? 3 :
|
|
|
|
|
config.snmp.upsModel === 'tripplite' ? 4 :
|
|
|
|
|
config.snmp.upsModel === 'liebert' ? 5 :
|
|
|
|
|
config.snmp.upsModel === 'custom' ? 6 : 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const defaultModelValue =
|
|
|
|
|
config.snmp.upsModel === 'cyberpower'
|
|
|
|
|
? 1
|
|
|
|
|
: config.snmp.upsModel === 'apc'
|
|
|
|
|
? 2
|
|
|
|
|
: config.snmp.upsModel === 'eaton'
|
|
|
|
|
? 3
|
|
|
|
|
: config.snmp.upsModel === 'tripplite'
|
|
|
|
|
? 4
|
|
|
|
|
: config.snmp.upsModel === 'liebert'
|
|
|
|
|
? 5
|
|
|
|
|
: config.snmp.upsModel === 'custom'
|
|
|
|
|
? 6
|
|
|
|
|
: 1;
|
|
|
|
|
|
|
|
|
|
const modelInput = await prompt(`Select UPS model [${defaultModelValue}]: `);
|
|
|
|
|
const modelValue = parseInt(modelInput, 10) || defaultModelValue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (modelValue === 1) {
|
|
|
|
|
config.snmp.upsModel = 'cyberpower';
|
|
|
|
|
} else if (modelValue === 2) {
|
|
|
|
@ -773,20 +837,20 @@ Options:
|
|
|
|
|
config.snmp.upsModel = 'custom';
|
|
|
|
|
console.log('\nEnter custom OIDs for your UPS:');
|
|
|
|
|
console.log('(Leave blank to use standard RFC 1628 OIDs as fallback)');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Custom OIDs
|
|
|
|
|
const powerStatusOID = await prompt('Power Status OID: ');
|
|
|
|
|
const batteryCapacityOID = await prompt('Battery Capacity OID: ');
|
|
|
|
|
const batteryRuntimeOID = await prompt('Battery Runtime OID: ');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create custom OIDs object
|
|
|
|
|
config.snmp.customOIDs = {
|
|
|
|
|
POWER_STATUS: powerStatusOID.trim(),
|
|
|
|
|
BATTERY_CAPACITY: batteryCapacityOID.trim(),
|
|
|
|
|
BATTERY_RUNTIME: batteryRuntimeOID.trim()
|
|
|
|
|
BATTERY_RUNTIME: batteryRuntimeOID.trim(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -799,8 +863,10 @@ Options:
|
|
|
|
|
console.log(`│ SNMP Host: ${config.snmp.host}:${config.snmp.port}`);
|
|
|
|
|
console.log(`│ SNMP Version: ${config.snmp.version}`);
|
|
|
|
|
console.log(`│ UPS Model: ${config.snmp.upsModel}`);
|
|
|
|
|
console.log(`│ Thresholds: ${config.thresholds.battery}% battery, ${config.thresholds.runtime} min runtime`);
|
|
|
|
|
console.log(`│ Check Interval: ${config.checkInterval/1000} seconds`);
|
|
|
|
|
console.log(
|
|
|
|
|
`│ Thresholds: ${config.thresholds.battery}% battery, ${config.thresholds.runtime} min runtime`
|
|
|
|
|
);
|
|
|
|
|
console.log(`│ Check Interval: ${config.checkInterval / 1000} seconds`);
|
|
|
|
|
console.log('└──────────────────────────────────────────┘\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -809,17 +875,22 @@ Options:
|
|
|
|
|
* @param config Current configuration
|
|
|
|
|
* @param prompt Function to prompt for user input
|
|
|
|
|
*/
|
|
|
|
|
private async optionallyTestConnection(config: any, prompt: (question: string) => Promise<string>): Promise<void> {
|
|
|
|
|
const testConnection = await prompt('Would you like to test the connection to your UPS? (y/N): ');
|
|
|
|
|
private async optionallyTestConnection(
|
|
|
|
|
config: any,
|
|
|
|
|
prompt: (question: string) => Promise<string>
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
const testConnection = await prompt(
|
|
|
|
|
'Would you like to test the connection to your UPS? (y/N): '
|
|
|
|
|
);
|
|
|
|
|
if (testConnection.toLowerCase() === 'y') {
|
|
|
|
|
console.log('\nTesting connection to UPS...');
|
|
|
|
|
try {
|
|
|
|
|
// Create a test config with a short timeout
|
|
|
|
|
const testConfig = {
|
|
|
|
|
const testConfig = {
|
|
|
|
|
...config.snmp,
|
|
|
|
|
timeout: Math.min(config.snmp.timeout, 10000) // Use at most 10 seconds for testing
|
|
|
|
|
timeout: Math.min(config.snmp.timeout, 10000), // Use at most 10 seconds for testing
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const status = await this.nupst.getSnmp().getUpsStatus(testConfig);
|
|
|
|
|
console.log('\n┌─ Connection Successful! ─────────────────┐');
|
|
|
|
|
console.log('│ UPS Status:');
|
|
|
|
@ -843,14 +914,15 @@ Options:
|
|
|
|
|
private async restartServiceIfRunning(): Promise<void> {
|
|
|
|
|
try {
|
|
|
|
|
// Check if the service is active
|
|
|
|
|
const isActive = execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
|
|
|
|
|
|
|
|
|
|
const isActive =
|
|
|
|
|
execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
|
|
|
|
|
|
|
|
|
|
if (isActive) {
|
|
|
|
|
// Service is running, restart it
|
|
|
|
|
console.log('┌─ Service Update ─────────────────────────┐');
|
|
|
|
|
console.log('│ Configuration has changed.');
|
|
|
|
|
console.log('│ Restarting NUPST service to apply changes...');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (process.getuid && process.getuid() === 0) {
|
|
|
|
|
// We have root access, restart directly
|
|
|
|
@ -866,7 +938,7 @@ Options:
|
|
|
|
|
console.log('│ You may need to restart the service manually:');
|
|
|
|
|
console.log('│ sudo systemctl restart nupst.service');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('└──────────────────────────────────────────┘');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
@ -878,18 +950,24 @@ Options:
|
|
|
|
|
* Optionally enable and start systemd service
|
|
|
|
|
* @param prompt Function to prompt for user input
|
|
|
|
|
*/
|
|
|
|
|
private async optionallyEnableService(prompt: (question: string) => Promise<string>): Promise<void> {
|
|
|
|
|
private async optionallyEnableService(
|
|
|
|
|
prompt: (question: string) => Promise<string>
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
if (process.getuid && process.getuid() !== 0) {
|
|
|
|
|
console.log('\nNote: Run "sudo nupst enable" to set up NUPST as a system service.');
|
|
|
|
|
} else {
|
|
|
|
|
const setupService = await prompt('Would you like to enable NUPST as a system service? (y/N): ');
|
|
|
|
|
const setupService = await prompt(
|
|
|
|
|
'Would you like to enable NUPST as a system service? (y/N): '
|
|
|
|
|
);
|
|
|
|
|
if (setupService.toLowerCase() === 'y') {
|
|
|
|
|
try {
|
|
|
|
|
await this.nupst.getSystemd().install();
|
|
|
|
|
console.log('Service installed and enabled to start on boot.');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Ask if the user wants to start the service now
|
|
|
|
|
const startService = await prompt('Would you like to start the NUPST service now? (Y/n): ');
|
|
|
|
|
const startService = await prompt(
|
|
|
|
|
'Would you like to start the NUPST service now? (Y/n): '
|
|
|
|
|
);
|
|
|
|
|
if (startService.toLowerCase() !== 'n') {
|
|
|
|
|
await this.nupst.getSystemd().start();
|
|
|
|
|
console.log('NUPST service started successfully.');
|
|
|
|
@ -902,7 +980,7 @@ Options:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Display the current configuration
|
|
|
|
|
*/
|
|
|
|
@ -914,68 +992,75 @@ Options:
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('┌─ Configuration Error ─────────────────────┐');
|
|
|
|
|
console.error('│ No configuration found.');
|
|
|
|
|
console.error('│ Please run \'nupst setup\' first to create a configuration.');
|
|
|
|
|
console.error("│ Please run 'nupst setup' first to create a configuration.");
|
|
|
|
|
console.error('└──────────────────────────────────────────┘');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get current configuration
|
|
|
|
|
const config = this.nupst.getDaemon().getConfig();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('┌─ NUPST Configuration ──────────────────────┐');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SNMP Settings
|
|
|
|
|
console.log('│ SNMP Settings:');
|
|
|
|
|
console.log(`│ Host: ${config.snmp.host}`);
|
|
|
|
|
console.log(`│ Port: ${config.snmp.port}`);
|
|
|
|
|
console.log(`│ Version: ${config.snmp.version}`);
|
|
|
|
|
console.log(`│ UPS Model: ${config.snmp.upsModel || 'cyberpower'}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (config.snmp.version === 1 || config.snmp.version === 2) {
|
|
|
|
|
console.log(`│ Community: ${config.snmp.community}`);
|
|
|
|
|
} else if (config.snmp.version === 3) {
|
|
|
|
|
console.log(`│ Security Level: ${config.snmp.securityLevel}`);
|
|
|
|
|
console.log(`│ Username: ${config.snmp.username}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Show auth and privacy details based on security level
|
|
|
|
|
if (config.snmp.securityLevel === 'authNoPriv' || config.snmp.securityLevel === 'authPriv') {
|
|
|
|
|
if (
|
|
|
|
|
config.snmp.securityLevel === 'authNoPriv' ||
|
|
|
|
|
config.snmp.securityLevel === 'authPriv'
|
|
|
|
|
) {
|
|
|
|
|
console.log(`│ Auth Protocol: ${config.snmp.authProtocol || 'None'}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (config.snmp.securityLevel === 'authPriv') {
|
|
|
|
|
console.log(`│ Privacy Protocol: ${config.snmp.privProtocol || 'None'}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Show timeout value
|
|
|
|
|
console.log(`│ Timeout: ${config.snmp.timeout / 1000} seconds`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Show OIDs if custom model is selected
|
|
|
|
|
if (config.snmp.upsModel === 'custom' && config.snmp.customOIDs) {
|
|
|
|
|
console.log('│ Custom OIDs:');
|
|
|
|
|
console.log(`│ Power Status: ${config.snmp.customOIDs.POWER_STATUS || 'Not set'}`);
|
|
|
|
|
console.log(`│ Battery Capacity: ${config.snmp.customOIDs.BATTERY_CAPACITY || 'Not set'}`);
|
|
|
|
|
console.log(
|
|
|
|
|
`│ Battery Capacity: ${config.snmp.customOIDs.BATTERY_CAPACITY || 'Not set'}`
|
|
|
|
|
);
|
|
|
|
|
console.log(`│ Battery Runtime: ${config.snmp.customOIDs.BATTERY_RUNTIME || 'Not set'}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Thresholds
|
|
|
|
|
console.log('│ Thresholds:');
|
|
|
|
|
console.log(`│ Battery: ${config.thresholds.battery}%`);
|
|
|
|
|
console.log(`│ Runtime: ${config.thresholds.runtime} minutes`);
|
|
|
|
|
console.log(`│ Check Interval: ${config.checkInterval / 1000} seconds`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Configuration file location
|
|
|
|
|
console.log('│');
|
|
|
|
|
console.log('│ Configuration File Location:');
|
|
|
|
|
console.log('│ /etc/nupst/config.json');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log('└──────────────────────────────────────────┘');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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 isActive =
|
|
|
|
|
execSync('systemctl is-active nupst.service || true').toString().trim() === 'active';
|
|
|
|
|
const isEnabled =
|
|
|
|
|
execSync('systemctl is-enabled nupst.service || true').toString().trim() === 'enabled';
|
|
|
|
|
|
|
|
|
|
console.log('┌─ Service Status ─────────────────────────┐');
|
|
|
|
|
console.log(`│ Service Active: ${isActive ? 'Yes' : 'No'}`);
|
|
|
|
|
console.log(`│ Service Enabled: ${isEnabled ? 'Yes' : 'No'}`);
|
|
|
|
@ -983,7 +1068,6 @@ Options:
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// Ignore errors checking service status
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`Failed to display configuration: ${error.message}`);
|
|
|
|
|
}
|
|
|
|
@ -999,12 +1083,12 @@ Options:
|
|
|
|
|
try {
|
|
|
|
|
// Import readline module for user input
|
|
|
|
|
const readline = await import('readline');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const rl = readline.createInterface({
|
|
|
|
|
input: process.stdin,
|
|
|
|
|
output: process.stdout
|
|
|
|
|
output: process.stdout,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Helper function to prompt for input
|
|
|
|
|
const prompt = (question: string): Promise<string> => {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
@ -1019,11 +1103,13 @@ Options:
|
|
|
|
|
console.log('This will completely remove NUPST from your system.\n');
|
|
|
|
|
|
|
|
|
|
// Ask about removing configuration
|
|
|
|
|
const removeConfig = await prompt('Do you want to remove the NUPST configuration files? (y/N): ');
|
|
|
|
|
|
|
|
|
|
const removeConfig = await prompt(
|
|
|
|
|
'Do you want to remove the NUPST configuration files? (y/N): '
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Find the uninstall.sh script location
|
|
|
|
|
let uninstallScriptPath: string;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Try to determine script location based on executable path
|
|
|
|
|
try {
|
|
|
|
|
// For ESM, we can use import.meta.url, but since we might be in CJS
|
|
|
|
@ -1031,16 +1117,13 @@ Options:
|
|
|
|
|
const binPath = process.argv[1];
|
|
|
|
|
const modulePath = dirname(dirname(binPath));
|
|
|
|
|
uninstallScriptPath = join(modulePath, 'uninstall.sh');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if the script exists
|
|
|
|
|
await fs.access(uninstallScriptPath);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// If we can't find it in the expected location, try common installation paths
|
|
|
|
|
const commonPaths = [
|
|
|
|
|
'/opt/nupst/uninstall.sh',
|
|
|
|
|
join(process.cwd(), 'uninstall.sh')
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const commonPaths = ['/opt/nupst/uninstall.sh', join(process.cwd(), 'uninstall.sh')];
|
|
|
|
|
|
|
|
|
|
for (const path of commonPaths) {
|
|
|
|
|
try {
|
|
|
|
|
await fs.access(path);
|
|
|
|
@ -1050,37 +1133,36 @@ Options:
|
|
|
|
|
// Continue to next path
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!uninstallScriptPath) {
|
|
|
|
|
console.error('Could not locate uninstall.sh script. Aborting uninstall.');
|
|
|
|
|
rl.close();
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Close readline before executing script
|
|
|
|
|
rl.close();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Execute uninstall.sh with the appropriate option
|
|
|
|
|
console.log(`\nRunning uninstaller from ${uninstallScriptPath}...`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Pass the configuration removal option as an environment variable
|
|
|
|
|
const env = {
|
|
|
|
|
...process.env,
|
|
|
|
|
REMOVE_CONFIG: removeConfig.toLowerCase() === 'y' ? 'yes' : 'no',
|
|
|
|
|
REMOVE_REPO: 'yes', // Always remove repo as requested
|
|
|
|
|
NUPST_CLI_CALL: 'true' // Flag to indicate this is being called from CLI
|
|
|
|
|
REMOVE_REPO: 'yes', // Always remove repo as requested
|
|
|
|
|
NUPST_CLI_CALL: 'true', // Flag to indicate this is being called from CLI
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Run the uninstall script with sudo
|
|
|
|
|
execSync(`sudo bash ${uninstallScriptPath}`, {
|
|
|
|
|
execSync(`sudo bash ${uninstallScriptPath}`, {
|
|
|
|
|
env,
|
|
|
|
|
stdio: 'inherit' // Show output in the terminal
|
|
|
|
|
stdio: 'inherit', // Show output in the terminal
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`Uninstall failed: ${error.message}`);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|